Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise
对象。
(1)对象的状态不受外界影响。Promise
对象代表一个异步操作,有三种状态:pending
(进行中)、fulfilled(已成功)和rejected
(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。
ES6 规定,Promise
对象是一个构造函数,用来生成Promise
实例。
const promiseA = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
Promise
构造函数接受一个函数作为参数,该函数的两个参数分别是resolve
和reject
。它们是两个函数,由 JavaScript 引擎提供,不用自己部署。调用promiseA函数时,必须执行resolve('成功参数')
或
reject('失败参数')
其中之一(可以不传参数)!
否则就一直是pending状态。
resolve
函数的作用是,将Promise
对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
reject
函数的作用是,将Promise
对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
Promise
实例生成以后,可以用then
方法分别指定resolved
状态和rejected
状态的回调函数。
promiseA.then(function(value) {
// success
}, function(error) {
// failure
});
then
方法可以接受两个回调函数作为参数。第一个回调函数是Promise
对象的状态变为resolved
时调用,第二个回调函数是Promise
对象的状态变为rejected
时调用。其中,第二个函数是可选的,不一定要提供。这两个函数都接受Promise
对象传出的值作为参数
const p1 = new Promise(function (resolve, reject) {
// ...
});
const p2 = new Promise(function (resolve, reject) {
// ...
resolve(p1);
})
这里,p1
的状态决定了p2
的状态。如果p1
的状态是pending
,那么p2
的回调函数就会等待p1
的状态改变;如果p1
的状态已经是resolved
或者rejected
,那么p2
的回调函数将会立刻执行。
then
方法是定义在原型对象Promise.prototype
上的。它的作用是为 Promise 实例添加状态改变时的回调函数。前面说过,then
方法的第一个参数是resolved
状态的回调函数,第二个参数(可选)是rejected
状态的回调函数。
采用链式的then
,可以指定一组按照次序调用的回调函数。执行顺序的规则是:
首先,同步的操作依次按照顺序执行,其次再是执行异步操作,且异步操作按照耗时多少的顺序输出结果。
var p=new Promise(function (resolve,reject) {
console.log('start')
setTimeout(function (){
console.log(111)
resolve();
}, 1000)
})
p.then(v=>{
console.log(222)
setTimeout(function () {
console.log(333)
}, 3000)
}).then(v=>{
console.log(444)
}).then(v=>{
setTimeout(function () {
console.log(555)
}, 2000)
console.log(666)
})
结果为:立即输出 start,1秒后输出111,再立即依次输出222,444,666,再过2秒输出555,再过1秒输出333
再看一个复杂的例子:
var p1=new Promise(function (resolve,reject) {
console.log('start')
setTimeout(function (){
console.log("000")
resolve();
}, 1000)
})
var p2=new Promise(function (resolve,reject) {
console.log('start2')
setTimeout(function (){
console.log(111)
resolve(p1);
}, 1000)
})
p2.then(v=>{
console.log(222)
setTimeout(function () {
console.log(333)
}, 4000)
}).then(v=>{
console.log(444)
}).then(v=>{
setTimeout(function () {
console.log(555)
}, 1000)
console.log(666)
})
p1.then(v=>{
console.log(777)
setTimeout(function () {
console.log(888)
}, 3000)
}).then(v=>{
console.log(999)
}).then(v=>{
setTimeout(function () {
console.log(101010)
}, 2000)
console.log(111111)
})
p1和p2都会立即执行,promise内部同步操作优先于异步操作,先输出start和start2,然后p2以p1的结果作为参数,所以会等待p1执行完(resolve或reject),再立即执行,即先等一秒立即输出 000,此时p1为resolve,立即执行then的同步操作,输出 777 999 111111,等待完成p1执行结束,再立即执行p2的同步操作,输出 111 222 444 666,此时同步操作全部执行完毕。接着执行异步操作,四个异步操作按耗时计算,依次输出555,101010,888,333
Promise.prototype.catch
方法是.then(null, rejection)
或.then(undefined, rejection)
的别名,用于指定发生错误时的回调函数。可以捕捉到promise内的错误和reject方法执行,以及它前面的then回调里面的错误。当promise内状态为reject时,它前面的then方法都不执行,直接执行catch方法,但是它之后的then方法不受影响秒回继续执行。
一般来说,不要在then
方法里面定义 Reject 状态的回调函数(即then
的第二个参数),总是使用catch
方法。
finally
方法用于指定不管 Promise 对象最后状态如何,都会执行的操作,与then和catch同一级别的位置。该方法是 ES2018 引入标准的。
Promise.all
方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.all([p1, p2, p3]);
p
的状态由p1
、p2
、p3
决定,分成两种情况。
(1)只有p1
、p2
、p3
的状态都变成fulfilled
,p
的状态才会变成fulfilled
,此时p1
、p2
、p3
的返回值组成一个数组,传递给p
的回调函数。
p.then([r1,r2,r3]=>{
console.log(r1,r2,r3)
}).catch(err=>{
console.log(err)
})
(2)只要p1
、p2
、p3
之中有一个被rejected
,p
的状态就变成rejected
,此时第一个被reject
的实例的返回值,会传递给p
的回调函数。
p.then(result=>{
console.log(r1,r2,r3)
}).catch(err=>{
console.log(err)
})
Promise.race
方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。但是与all不同的是,只要p1
、p2
、p3
之中有一个实例率先改变状态,p
的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p
的回调函数。
Promise.resolve
方法可以将现有对象转为 Promise 对象,然后就可以使用then回调的链式调用。
参考:http://es6.ruanyifeng.com/#docs/promise