前言
接下来要深入的是 then 链式调用,这个是实现中最绕的一块。在解读之前,我们再加深一下印象。
constructor -> fn --同步--> resolve(reject) -> then -> then 回调
constructor -> fn --异步--> then -> resolve(reject) -> then 回调
复制代码
无论是同步还是异步的情况,then 回调函数
都会在 resolve
执行之后,才会执行。所以可以这样理解,只有执行了 resolve
之后,才会触发 then 回调函数
的执行。
注:本次阅读的是 then/promise 的 3.0.0 版本,源码请戳 这里。
解读
先来看下怎么示例代码:
var p1 = new Promise(function (resolve) {
setTimeout(function () {
resolve(1);
}, 1000);
})
p1.then(function (val) {
var p3 = new Promise(function (resolve) {
setTimeout(function () {
resolve(val + 1);
}, 1000);
});
return p3;
}).then(function (val) {
console.log(val);
});
复制代码
看到上面的代码,我们能显示地看到 new 了两个 Promise 实例,分别用 p1
和 p3
表示。然后再从之前 then 方法的实现中,我们会看到它也 return 了一个 Promise 实例,我们用 p2
来表示 then 函数执行后返回的 Promise 实例,以便接下来的区分。
this.then = function(onFulfilled, onRejected) {
return new Promise(function(resolve, reject) {
handle({ onFulfilled: onFulfilled, onRejected: onRejected, resolve: resolve, reject: reject })
})
}
复制代码
对着上面的代码,我们再看下变量分别表示着啥:
- p1:最外层的 Promise 实例;
- p2:p1 调用 then 函数返回的 Promise 实例;
- p3:p1 的 then 回调函数返回的 Promise 实例。
执行顺序
理一下上面示例代码的执行顺序:
- new 了一个 Promise 实例赋值给 p1,并执行 fn 函数;
- p1 调用它的 then 函数,返回 p2;
- p2 调用它的 then 函数(实际上又返回一个新的 Promise 实例);
- 一秒后,执行 p1 的 resolve 函数;
- 触发 p1 的 then 回调函数,此时 p3 被创建并返回;
- 再一秒后,p3 执行 resolve 函数;
- 触发 p2 的 then 回调函数。
疑惑:这里 p3 执行了它的 resolve 函数,怎么会触发 p2 的 then 回调函数,不应该是触发自己的 then 回调函数的吗??
handle
从第 5 步开始看代码,触发 p1 的 then 回调函数,也就会触发 p1 里的 handle 函数:
function handle(deferred) {
nextTick(function() {
var cb = state ? deferred.onFulfilled : deferred.onRejected
if (typeof cb !== 'function'){
(state ? deferred.resolve : deferred.reject)(value)
return
}
var ret
try {
ret = cb(value)
}
catch (e) {
deferred.reject(e)
return
}
deferred.resolve(ret)
})
}
复制代码
首先会执行 onFulfilled
函数,即 p1
的 then 回调函数,此时创建 p3
返回赋值给 ret。然后再调用 deferred 的 resolve,其实是调用了 p2
的 resolve,将 p3
作为参数传入。
resolve
deferred 调用 resolve,将不会执行的代码先去掉:
function resolve(newValue) {
if (delegating)
return
resolve_(newValue)
}
function resolve_(newValue) {
if (state !== null)
return
try {
if (newValue === self) throw new TypeError('A promise cannot be resolved with itself.')
if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) {
var then = newValue.then
if (typeof then === 'function') {
delegating = true
then.call(newValue, resolve_, reject_)
return
}
}
} catch (e) { reject_(e) }
}
复制代码
因为 newValue 就是 p3
,判断它是一个带有 then 的对象后,会调用它的 then 函数,将 p2
的 resolve_ 作为 p3
的 then 回调函数传入。
如果 p3
执行了 resolve,即会执行它的 then 回调函数,即 p2
的 resolve_。如果 p2
执行了 resolve_,即会执行自己的 then 回调函数。
这里可以把代码视为这样,resolve_ 里的变量都是 p2
的变量:
then.call(newValue, function resolve_ (newValue) {
// 更新状态
state = true
// 记住 resolve 的参数
value = newValue
finale()
}, reject_);
复制代码
而 p2
的 then 回调函数,就是示例代码里那个链式调用的 then 的回调函数。所以就实现了 then 的链式调用。
说了上面一堆话,可能有点难理解,来张图配合理解一下吧。
delegating
简单讲一下变量 delegating
,因为 p2
的 resolve_ 可以通过调用自己 resolve 来触发,也可以通过 p3
的 then 回调来触发。当通过 p3
的 then 回调来触发时,delegating
将设置为 true,此时就切断了 p2
通过调用自己 resolve 来触发的情况。
function resolve(newValue) {
if (delegating)
return
resolve_(newValue)
}
复制代码
nextTick
在知道了整个 then 链式调用的顺序之后,最后来看看 nextTick
。以我的理解,使用 nextTick
是为了让 then 回调函数在所以的 then 代码执行后才会执行。
否则,以下代码,p1
回调函数先执行,再执行第二个 then 函数,这就违背了 ES6 中 Promise 是微观任务队列的概念了。
var p1 = new Promise(function (resolve) {
resolve(1);
})
p1.then(function (val) {
var p3 = new Promise(function (resolve) {
resolve(val + 1);
});
return p3;
}).then(function (val) {
console.log(val);
});
复制代码
总结
Promise 的 then 链式调用,简单来说就是用一个新的 Promise 实例来做桥梁,当前面 then 回调函数里返回的 Promise 执行 resolve,就会触发桥梁去执行它的 then 回调。
如果有点难理解,莫着急,重新多看几遍文章理解一下。