浅析Promise

  • 白小霁
  • 16 Minutes
  • April 29, 2017

Promise 的含义

Promise表示一个异步操作的最终结果。简单说,Promise保存着某个未来才会结束的事件(一般来说就是异步操作)的结果。

从语法上说,Promise是一个对象,从它可以获取异步操作的消息。
—— 《ECMAScript 6 入门》

Promise/A+ 规范

这个规范规定了Promise的一些规范是需要如何实现。参考文章

术语

thenable是一个包含then方法的对象或是函数。

Promise状态

then方法

使用Promise

Promise已经在ES 6中原生实现,是一个构造函数,用来生成Promise实例。

1
2
3
4
5
6
7
8
let promise = new Promise((resolve,reject)=>{
let randomNum = Math.random()
if(randomNum>0.5){
resolve("大于0.5",randomNum)
}else{
reject("小于0.5",randomNum)
}
})

Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolvereject。他们是两个函数,有JavaScript引擎提供,不用自己部署。

resolve函数的作用是:就是将Promise对象的状态从「等待」到「完成」(即:pending -> resolve),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;reject函数的作用,就是将Promise对象的状态从「等待」到「失败」(即:pending -> rejected),在异步操作失败时调用,并将异步操作的报出的错误,作为参数传递出去

Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。

1
2
3
4
5
promise.then(function(val){
console.log(val)
},function(error){
console.log(error)
})

可一般不鼓励then方法设置两个回调函数,所以在Promise的原型链上有一个catch方法来处理发生错误时的回调,所以上面代码可以改写成:

1
2
3
4
5
promise.then(function(val){
console.log(val)
}).catch(function(error){
console.log(error)
})

这样做的好处是:

Promise.resolve()

有时候需要将现有的对象转化为Promise对象,就要使用此方法。

1
2
3
Promise.resolve("baixiaoji")
//等价于
new Promise(resolve => resolve("baixiaoji"))

该方法会根据参数不同,会返回不同状态的Promise对象,不要单纯的认为该方法就是返回resolved状态的Promise对象

上面代码中,thenable对象的then方法执行后,对象p1的状态就变为resolved,从而立即执行最后那个then方法指定的回调函数,输出成功;当然如果thenable对象里面的then方法是写reject("失败")的话,对象p1的状态就变为rejected

需要注意的是,立即resolve的Promise对象,是在本轮“事件循环”(event loop)的结束时,而不是在下一轮“事件循环”的开始时。 —— 《ECMAScript 6 入门》

Promise.reject()

Promise.reject(reson)方法也会返回一个新的Promise实例,该实例的状态为rejected

注意,Promise.reject()方法的参数,会原封不动地作为reject的理由,变成后续方法的参数。这一点与Promise.resolve方法不一致。 —— 《ECMAScript 6 入门》

请记住上面这句话,所以有了下面这个案例

1
2
3
4
5
6
7
8
9
10
const thenable = {
then(resolve, reject) {
reject('出错了');
}
};
Promise.reject(thenable)
.catch(e => {
console.log(e === thenable) // logs true
})

上面代码中,Promise.reject方法的参数是一个thenable对象,执行以后,后面catch方法的参数不是reject抛出的「出错了」这个字符串,而是thenable对象。


遇见这样一个需求,根据三个异步(p1、p2、p3)操作的结果在进行操作

Promise.all()

该方法用于将多个Promise实例,包装成一个新的Promise实例

1
let p = Promise.all([p1,p2,p3])

上面的代码中,Promise.all方法接受一个数组作为参数,首先要保证p[1-3]这三个参数是Promise对象的实例,入如果不是,就会想调用Promise.resolve方法,将参数转化为Promise对象,再进一步处理。

Promise.all方法的参数可以不是数组,但必须具有Iterator接口,且返回的每个成员都是Promise实例 —— 《ECMAScript 6 入门》

p的状态就有这三个Promise对象决定,有一下两种情况:

Promise.race()

同样是将多个Promise实例,包装成一个新的Promise实例

1
let p = Promise.race([p1,p2,p3])

上面的代码,只要p1p2p3之中的一个实例率先改变了状态,p的状态就跟着改变。那个率先改变的Promise实例的返回值,就是传递给p的回调函数。

Promise.race方法的参数和Promise.all方法的参数一样,如果不是Promise实例,就调用Promise.resolve方法,将参数转化为Promise对象,再进一步处理。


ECMAScript 6 入门 —— Promise