最近一次面试被问到Promise链式调用原理,然而自己对promise的理解还是很浅显的,只了解其使用方法和api,对其实现原理一无所知
前言
静下心来分析。因为本人js稍弱,分析过程加深我对作用域、闭包的理解。
也非常幸运,能找到这篇由浅至深分析的博客
https://mengera88.github.io/2017/05/18/Promise原理解析/ 各位童鞋们可以先看博客,最后如果分析链式调用时遇到困难的时候,可以看看我这篇博客,看能不能提供新思路。我只归纳下最后链式调用原理
链式调用
代码
简单实现,并不是源码
function Promise(fn) {
var callbacks = [];
var status = 'pending';
var value = null;
this.then = onFulfilled => {
return new Promise(function (resolve) {
handle({
onFulfilled: onFulfilled || null, // resolve后需要执行的回调函数
resolve: resolve // 当前Promise(bridge) 的resolve
});
})
}
function handle(callback) {
if (status === 'pending') {
callbacks.push(callback); // callbacks是调用then的promise作用域里的,存储then中回调函数,不会是then返回的新promise
// console.log(callback.onFulfilled.toString());
return;
}
if (!callback.onFulfilled) {
callback.resolve(value);
return;
}
var res = callback.onFulfilled(value);
callback.resolve(res); // resolve为Promise(bridge)
}
function resolve(res) {
if (res && (typeof res === 'object' || typeof res === 'function')) {
var then = res.then;
if (typeof then === 'function') {
then.call(res, resolve); // 注册回调函数中返回的promise(后邻),resolve为Promise(bridge)
return;
}
}
status = 'fulfilled';
value = res;
setTimeout(() => {
callbacks.forEach(cb => {
handle(cb);
});
})
}
fn(resolve);
}
function getUserId() {
return new Promise(function (resolve) {
setTimeout(() => {
resolve('111111');
})
})
}
function getUserJobById(id) {
console.log(id);
return new Promise(function (resolve) {
setTimeout(() => {
resolve('do some');
})
})
}
getUserId()
.then(getUserJobById)
.then(function (job) {
console.log(job);
})
注意
- 根据执行promise时的逻辑来推演
- 开始分析的时候可以不注意resolve是哪个Promise的,重点关注callbacks的值
- 创建Promise实例时的函数会被立即执行
- 主要使用了设计模式中的观察者模式
- promise里面的then函数仅仅是注册了后续需要执行的代码,真正的执行是在resolve方法里面执行的
- 注意 bridge Promise 的作用,用于连接(储存)后邻Promise resolve后需要执行的回调函数
- 后邻Promise既本例中的getUserJobById Promise,其callbacks保存的并不是其resolve后需要执行的回调函数,而是bridge Promise的resolve,bridge Promise的callbacks保存了getUserJobById Promise需要执行的回调,这样就形成了链式关系。通过不同resolve来访问不同Promise的作用域,遍历相应的callbacks。为什么不是getUserJobById Promise来保存其需执行的回掉而用bridge Promise来保存,下文会分析原因
时间节点分析
和上文提到博客的分析角度不同,我以resolve的时间节点进行分析
- t1时间点。上述代码执行完毕,但getUserId Promise还没有resolve。上图只说明了getUserId Promise和bridge Promise1在这个时候被创建。其实,如果有更多的then,连接其他then的bridge Promise在这个时候也都已经被创建了。重点关注此时两个Promise的callbacks,getUserId Promise的callbacks,存储了getUserJobById函数。bridge Promise1的callbacks,存储了后邻Promise即getUserJobById Promise resolve后需执行的回调函数
- t2时间点。getUserId Promise resolve(‘11111’)后但getUserJobById Promise还没resolve。遍历getUserId Promise的callbacks,getUserJobById执行,生成了getUserJobById Promise。其callbacks保存了bridge Promise的resolve。
- t3时间点。getUserJobById Promise resolve(‘do some’)。遍历getUserJobById Promise的callbacks。之后的过程同注意的最后一点。
为什么需要 bridge Promise
getUserId()
.then(getUserJobById)
.then(function (job) {
console.log(job);
})
为了满足这种写法,then之间必须通过Promise来连接,但前邻Promise没有resolve之前是无法得到后邻Promise的,所有需要bridge Promise来进行连接