Promise
1、Promise是什么?
Promise 是异步编程的一种解决方案:从语法上讲,promise是一个对象,从它可以获取异步操作的消息;从本意上讲,它是承诺,承诺它过一段时间会给你一个结果。promise有三种状态: pending(等待态),fulfiled(成功态),rejected(失败态);状态一旦改变,就不会再变。创造promise实例后,它会立即执行。
相信大家经常会写这样的代码:
// 当参数a大于10且参数fn2是一个方法时 执行fn2
function fn1(a, fn2) {
if (a > 10 && typeof fn2 == 'function') {
fn2()
}
}
fn1(11, function() {
console.log('this is a callback')
})
一般来说我们会碰到的回调嵌套都不会很多,一般就一到两级,但是某些情况下,回调嵌套很多时,代码就会非常繁琐,会给我们的编程带来很多的麻烦,这种情况俗称——回调地狱。
这时候我们的promise就应运而生了!
promise是用来解决两个问题的:
- 回调地狱,代码难以维护, 常常第一个的函数的输出是第二个函数的输入这种现象
- promise可以支持多个并发的请求,获取并发请求中的数据
- 这个promise可以解决异步的问题,本身不能说promise是异步的
简单理解:
- promise是js在进行异步编程的解决方案
- promise支持链式调用,可以解决回调地狱问题!
- promise用来封装一个异步操作并可以获取其成功/失败的结果值!
## 异步编程
* fs 文件操作
require('fs').readfile('./index.html',(err,data)=>{})
* 数据库操作
* AJAX
$.get('./server',(data)=>{})
* 定时器
setTimeout(()=>{},2000)
2、Promise的API
- .then() 获取promise的结果
- .catch() 来获取失败时返出的错误信息
- Promise.all( ) 接受多个promise的实例做为参数,参数必须是一个数组,promise都执行成功后,返回的是执行后的结果的一个数组,如果有一个失败了,返回的是第一个失败的返回值
- Promise.race( ) 接受多个promise的实例做为参数,其中一个promise完成时执行,返回结果是第一个完成的promise的返回数据
3、es6 promise用法大全
Promise是一个构造函数,自己身上有all、reject、resolve这几个眼熟的方法,原型上有then、catch等同样很眼熟的方法。
简单写一个小案例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="container">
<h2 class="page-handler">Promise 初体验 </h2>
<button class="btn btn-primary" id="btn">点击抽奖</button>
</div>
<script>
// 生成随机数
function rand(m,n){
return Math.ceil(Math.random() * ((n-m+1)) +m+1)
}
/*
点击按钮 1s后显示是否中奖(中奖概率为30%)
若是中奖 弹出 恭喜您中奖了
若是没有中奖 显示再接再厉
*/
//获取元素对象
const btn = document.querySelector('#btn');
// 绑定单击事件
btn.addEventListener('click',()=>{
// 定时器
// setTimeout(() => {
// // 30% 1-100
// // 获取1-100 的一个随机数
// let n = rand(1,100);
// if(n <= 30){
// alert('恭喜您中奖了')
// }else{
// alert('再接再厉')
// }
// }, 100);
// promise 形式解决!
// resolve 解决 reject 拒绝 都是函数类型的数据!
const p = new Promise((resolve,reject)=>{
setTimeout(() => {
// 30% 1-100
// 获取1-100 的一个随机数
let n = rand(1,100);
if(n <= 30){ // 这里如果要加一个小需求,那就是将我们中奖或者失败的数字返回!
resolve(n) // 将promise的状态设置为 成功 !
}else{
reject(n) // 将promise的状态设置为 失败!
}
}, 100);
})
// 调用then 方法
p.then((value)=>{
alert('中奖了'+value)
},(reason)=>{
alert('再接再厉!'+reason)
})
})
</script>
</body>
</html>
Promise的构造函数接收一个参数:函数,并且这个函数需要传入两个参数:
- resolve :异步操作执行成功后的回调函数
- reject:异步操作执行失败后的回调函数
Promise 状态:(Promise对象中的一个属性[PromiseState])
- pending 未决定的
- resolved / fullfilled 成功
- rejected 失败
Promise 返回值:(PromiseResult)保存着异步任务成功或失败的结果!
- resolve
- reject
promise对文件的练习:
const fs = require('fs')
//回调函数的形式!
// fs.readFile('./word.txt',(err,data)=>{
// // 如果出错,则抛出错误!
// if (err) throw err;
// // 输出文件内容!
// console.log(data.toString())
// })
// promise 形式
const p = new Promise((resolve,reject) => {
fs.readFile('./word.txt',(err,data)=>{
if (err) reject()
resolve(data)
})
})
// 调用then
p.then(value=>{
console.log(value.toString());
},reason=>{
console.log(reason);
})
then 链式操作的用法
所以,从表面上看,Promise只是能够简化层层回调的写法,而实质上,Promise的精髓是“状态”,用维护状态、传递状态的方式来使得回调函数能够及时调用,它比传递callback函数要简单、灵活的多。所以使用Promise的正确场景是这样的:
p.then((data) => {
console.log(data);
})
.then((data) => {
console.log(data);
})
.then((data) => {
console.log(data);
});
reject的用法 :
把Promise的状态置为rejected,这样我们在then中就能捕捉到,然后执行“失败”情况的回调。
then中传了两个参数,then方法可以接受两个参数,第一个对应resolve的回调,第二个对应reject的回调。所以我们能够分别拿到他们传过来的数据。
let p = new Promise((resolve, reject) => {
//做一些异步操作
setTimeout(function(){
var num = Math.ceil(Math.random()*10); //生成1-10的随机数
if(num<=5){
resolve(num);
}
else{
reject('数字太大了');
}
}, 2000);
});
p.then((data) => {
console.log('resolved',data);
},(err) => {
console.log('rejected',err);
}
);
catch的用法
我们知道Promise对象除了then方法,还有一个catch方法,它是做什么用的呢?其实它和then的第二个参数一样,用来指定reject的回调。
p.then((data) => {
console.log('resolved',data);
}).catch((err) => {
console.log('rejected',err);
});
效果和写在then的第二个参数里面一样。不过它还有另外一个作用:在执行resolve的回调(也就是上面then中的第一个参数)时,如果抛出异常了(代码出错了),那么并不会报错卡死js,而是会进到这个catch方法中。请看下面的代码:
p.then((data) => {
console.log('resolved',data);
console.log(somedata); //此处的somedata未定义
})
.catch((err) => {
console.log('rejected',err);
});
在resolve的回调中,我们console.log(somedata);而somedata这个变量是没有被定义的。如果我们不用Promise,代码运行到这里就直接在控制台报错了,不往下运行了。
也就是说进到catch方法里面去了,而且把错误原因传到了reason参数中。即便是有错误的代码也不会报错了,这与我们的try/catch语句有相同的功能
all的用法:
谁跑的慢,以谁为准执行回调。all接收一个数组参数,里面的值最终都算返回Promise对象
Promise的all方法提供了并行执行异步操作的能力,并且在所有异步操作执行完后才执行回调。看下面的例子:
let Promise1 = new Promise(function(resolve, reject){})
let Promise2 = new Promise(function(resolve, reject){})
let Promise3 = new Promise(function(resolve, reject){})
let p = Promise.all([Promise1, Promise2, Promise3])
p.then(funciton(){
// 三个都成功则成功
}, function(){
// 只要有失败,则失败
})
有了all,你就可以并行执行多个异步操作,并且在一个回调中处理所有的返回数据,是不是很酷?*有一个场景是很适合用这个的,一些游戏类的素材比较多的应用,打开网页时,预先加载需要用到的各种资源如图片、flash以及各种静态文件。所有的都加载完后,我们再进行页面的初始化。
race的用法:谁跑的快,以谁为准执行回调
race的使用场景:比如我们可以用race给某个异步请求设置超时时间,并且在超时后执行相应的操作,代码如下:
//请求某个图片资源
function requestImg(){
var p = new Promise((resolve, reject) => {
var img = new Image();
img.onload = function(){
resolve(img);
}
img.src = '图片的路径';
});
return p;
}
//延时函数,用于给请求计时
function timeout(){
var p = new Promise((resolve, reject) => {
setTimeout(() => {
reject('图片请求超时');
}, 5000);
});
return p;
}
Promise.race([requestImg(), timeout()]).then((data) =>{
console.log(data);
}).catch((err) => {
console.log(err);
});
requestImg函数会异步请求一张图片,我把地址写为"图片的路径",所以肯定是无法成功请求到的。timeout函数是一个延时5秒的异步操作。我们把这两个返回Promise对象的函数放进race,于是他俩就会赛跑,如果5秒之内图片请求成功了,那么遍进入then方法,执行正常的流程。如果5秒钟图片还未成功返回,那么timeout就跑赢了,则进入catch,报出“图片请求超时”的信息。
4、async/await的区别
1、什么是async/await
说明:同样是解决异步操作,promise是ES6语法,async和await是ES7语法
1、返回值是promise对象。
2、在使用异步的操作前+await,函数写async关键字。
3、await只有再含async的函数中才生效。
4、想让其为异步操作,返回值变为promise。
5、出现了await关键之之后的代码都要在其执行之后才会执行(下图的wwww要等await执行之后才会执行)
async/await是写异步代码的新方式,它是generator的语法糖,以前的方法有回调函数和 Promise。async/await是基于Promise实现的,它不能用于普通的回调函数。
async/await与Promise一样,是非阻塞的。async/await使得异步代码看起来像同步代码,这正是它的魔力所在。
2、什么是Promise
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。所谓promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。
async/await语法
- 使用Promise
Promise.resolve().then(data=>{//promise
console.log(data)
return "aaa"
})
- 使用Async
async function foo() {
console.log(await getJSON)
return "aaa"
}
foo();
区别
- 函数前面多了一个async关键字。await关键字只能用于async定于的函数内。async函数会隐式地返回一个Promise,该promise的resolve值就是return的值。示例中resolve的值就是字符串"aaa"
- await的必须在异步函数中使用;其中await有限制(不允许出现在箭头函数中;不允许出现在同步函数声明中;不允许出现在同步函数表达式中;如果在同步函数中使用await就会抛出SyntaxError。)
//不允许出现在箭头函数中
function foo(){
const syncFn = () => {
return await Promise.resolve('foo');
};
console.log(syncFn());
}
//不允许出现在同步函数声明中
function bar(){
const syncFn=function () {
return await Promise.resolve('bar');
};
console.log(syncFn());
}
//不允许出现在同步函数表达式中
function baz(){
const syncFn=function () {
return await Promise.resolve('baz');
};
console.log(syncFn());
}
为什么async/await更好?
- 使用async函数可以让代码简洁很多,不需要想Promise一样需要then,不需要写匿名函数处理Promise的resolve的值,也不需要定义多余的data变量,还避免了嵌套代码。
- async/await 让 try/catch可以同时处理同步和异步的错误。在下面的示例中,try/catch不能处理JSON.parse的错误,因为它在Promise中,我们需要使用.catch,这样的错误会显得代码非常的冗余。
const makeRequest = () => {
try {
getJSON().then(result => {
// JSON.parse可能会出错
const data = JSON.parse(result)
console.log(data)
})
// 取消注释,处理异步代码的错误
// .catch((err) => {
// console.log(err)
// })
} catch (err) {
console.log(err)
}
}
使用async/await
的话,catch
能处理JSON.parse
错误
const makeRequest = async () => {
try {
// this parse may fail
const data = JSON.parse(await getJSON())
console.log(data)
} catch (err) {
console.log(err)
}
}