背景
看到一个Promise的题目:
new Promise((resolve, reject) => {
console.log('promise1')
resolve()
})
.then(() => {
console.log('then11')
new Promise((resolve, reject) => {
console.log('promise2')
resolve()
})
.then(() => {
console.log('then21')
})
.then(() => {
console.log('then23')
})
})
.then(() => {
console.log('then12')
})
本以为是输出顺序为: promise1, then11, promise2, then21, then23, then12, 实际却有些偏差: promise1, then11, promise2, then21, then12
, then23
。有些出乎认知,于是乎找了es6-promise了解了解其实现的原理。
Promise基本概念介绍
- Promise状态:有pending、fulfilled(resolved个人习惯叫这个)、rejected三种状态,初始为pending。
- Promise用法:如上支持链式调用,异常可以通过
.catch
获取.then
的第二个回调捕获,还支持一些finally, race等操作 - Promise解决的问题:我理解主要是解决异步回调,异步代码异常捕获问题。
实现过程
Promise的实现
Promise支持链式调用,每一次调用都会新初始一个Promise,然后其onFulfill或者onRjection的回调函数的返回值都将在新返回的Promise中的result属性中存储。
也就是说明有多少个链式调用,就会出现对应次数的Promise实例数:
new Promise(resolve => {
resolve()
}).then(function() {
return 1
}).then(function(){
return 2
})
对应的Promise链为:
rootPromise (new Promise生成的实例) -> child1Promise(第一个then) -> child2Promise(第二个then产生的)
每一个Promise都会记录其then或者catch方法传入的参数,通过微任务形式触发。
回到文档开头的例子, 将then中的 onFulfill 回调添加名称:
new Promise((resolve, reject) => { // 该实例命名为 A1 callback中会有 then1
console.log('promise1')
resolve()
})
// 这个then命名 A1then
.then(function then1(){ // 新返回的一个空的Promise实例 A11,callback中会有 then12
console.log('then11')
new Promise((resolve, reject) => { // 该实例命名为B1 callback 会存在 then21
console.log('promise2')
resolve()
})
// B1then
.then(function then21() { // 新返回的一个空的Promise实例B12,callback中会有 then23
console.log('then21')
})
// B12then
.then(function then23() {
console.log('then23')
})
})
// A11then
.then(function then12() {
console.log('then12')
})
- 首先执行 A1 对应的代码,执行A1then方法时,发现 A1 已经 resovle 了,then1 函数被放在微任务队列中,等待执行,注意此时A11then只是被添加到A11的callback中,并不会被放到微任务队列中。
- 开始执行微任务,执行 then1 函数,执行过程中会同步创建一个 B1 的实例,同时也被resolve了,执行其 then 方法,会将其回调 then21放到微任务队列中,此时其第二个then(B12then)并不会被放到微任务队列中。接下来执行 A11then, 将then12也放入微任务队列中。
- 执行then21,修改对应Promise的状态 然后将 then23放入微任务队列中
- 执行 then12
- 执行 then23
最终的输出为 promise1, then11, promise2, then21, then12, then23;
Promise的链式调用,和我们常规的同步链式调用会不一样,不是一次性执行完,而是一个递归的过程,每次都会根据当前Promise的状态,决定是否需要将其callback放入到微任务队列中。
关于放入微任务队列,并不是一个主动行为,但可以将callback作为processs.nextTick(Node中的形式,浏览器可以通过MutationObserver方式)的回调函数,间接达到目的。
总结
通过看 es6-promise 的源码大致了解Promise的运行机制,里面也有很多不同的场景的适配,看得过程也建议手写些代码,增强对代码的理解。
Promise的运行机制需要结合promise状态和回调(then/catch的回调)函数结合理解,首先会在promises实例中暂存回调,如果有Promise状态切换了,则将其回调添加到微任务队列中,等待被执行。