Promise执行顺序的补充

开发·学习 · 2023-10-18 · 69 人浏览

前言

先看一段代码

let p1 = Promise.resolve();
// 1. p1 的状态为 fulfilled

let p2 = p1.then(() => {
  console.log(0);
  let p3 = Promise.resolve(4);
  return p3;
});
// 2. 因为 p1 的状态已经是 fulfilled,所以调用 then 后立即将 onFulfilled 放入 microtask 队列
// 此时 microtask 只有p1的 onFulfilled: [p1.onFulfilled]

let p4 = p2.then((res) => {
  console.log(res);
});

// 3. p2的状态还是 pending,所以调用 then 后是为 p2 收集依赖,
// 此时 p2 的 reactions 如下
/*{
    onFulfilled: (res) => {console.log(res)},
    onRejected: undefined
}*/

let p5 = Promise.resolve();
// 4. p5 的状态为 fulfilled

let p6 = p5.then(() => {
  console.log(1);
});
// 5. 同第2步,将 onFulfilled 加入 microtask 队列
// 此时 microtask 是: [p1.onFulfilled, p5.onFulfilled]

let p7 = p6.then(() => {
  console.log(2);
});
// 6. 同第3步,是给 p6 添加 reactions

let p8 = p7.then(() => {
  console.log(3);
});
// 7. 同上,是给 p7 添加 reactions

let p9 = p8.then(() => {
  console.log(5);
});
// 8. 同上,是给 p8 添加 reactions

let p10 = p9.then(() => {
  console.log(6);
});
// 9. 同上,是给 p9 添加 reactions

代码中已经增加了注释,其中出处来自:
知乎- Promise.then

问题点

如果按照正常流程来看, 由于Promise的状态改变,当其为fulfilled时,会将thenonFulfilled回调加入到微任务队列中,那么代码执行完成后, 微任务队列中就存在 p2.onFulfilled以及p6.onFulfilled 两个任务。

如果p2中的回调是正常返回值或者没有返回值,默认底层会返回一个新的Promise给后续的then处理,那么此时顺序应该是:
0 1 4 2 3 5 6, 但代码最终输出的是 0 1 2 3 4 5 6

但由于p2的执行,内部返回的是一个Promise.resolve, 由知乎文章内大佬们的解释可得知,最终这块代码会被解释为:

let promiseResolveThenableJobTask = () => {
    p3.then((value) => { 
        ReslovePromise(p2, value) 
    })
}

并重新被推入到队列中,所以队列的变化是这样是:

[p2.onFulfiled,p6.onFulfilled] => [p6.onFulfilled,promiseResolveThenableJobTask ]

此时输出了0,接着p6被解析输出了1,此时Promise被处理交由下一个p7处理,此时队列变为

[promiseResolveThenableJobTask,p7.onFulfilled]

被包装的promiseResolveThenableJobTask,最终执行的是前面解释的代码p3.then,那么接着队列中由重新推入了一个任务
ReslovePromise(p2, value),并且value此时为4

[p7.onFulfilled,ReslovePromise(p2, value)]

接着执行p7输出2,此时任务交给ReslovePromise,它再次执行p2本身并传入value,此时实际才会交给到p4的执行,此时又再次进入队列,并且p7执行完后交给下一个then执行

[p8.onFulfilled,p4.onFulfiled]

此时输出3,然后输出4

总结

由于返回的是一个新的Promise.resovle,在ECMA标准中,它会被包装成一个新的函数执行,并在上一个微任务执行时,作为新的微任务推入到队列,最终会导致产生两次微任务才会流转到下一个then的调用

Promise 异步
Theme Jasmine by Kent Liao