Promise的resolve/reject何时真正添加微任务?为何测试代码执行顺序与预期不符?
Promise的resolve/reject何时真正添加微任务?为何测试代码执行顺序与预期不符?
哈哈,这个问题踩中了很多人对Promise微任务机制的误解点!我来帮你理清楚到底发生了什么~
你之前的预期是错的,核心原因是你误解了微任务的添加时机:你以为外层Promise的resolve()一调用,就会把它的then回调(打印2的逻辑)立刻塞进微任务队列,但实际上,微任务的添加和then回调的注册时机是强绑定的——不是resolve()触发就自动加,而是要等你调用then()注册回调时,才会根据Promise当前的状态决定要不要把回调加入微任务队列。
我们拿你第二个代码片段来拆解完整的执行流程,就能瞬间明白:
new Promise((resolve) => { resolve(); new Promise((r) => { r(); }).then(() => { console.log("1") }); }).then(() => { console.log("2") })
执行步骤全解析
主线程同步执行阶段
- 首先执行
new Promise(executor),立刻调用它的executor函数:- 调用
resolve():外层Promise状态变为fulfilled,但此时外层Promise还没有注册任何then回调(因为链式调用的then()是在executor函数完全执行完之后才会被调用的),所以这一步不会添加任何微任务。 - 接着创建内层Promise,执行它的executor并调用
r():内层Promise状态变为fulfilled。 - 调用内层Promise的
then():此时内层Promise已经是fulfilled状态,所以打印1的回调会立刻被加入微任务队列。
- 调用
- 等外层Promise的executor函数执行完毕,主线程才会执行外层Promise的
then()方法,注册打印2的回调。此时外层Promise已经是fulfilled状态,所以这个回调会被加入微任务队列的末尾。
- 首先执行
微任务队列执行阶段
主线程的同步代码全部执行完成后,开始按FIFO顺序处理微任务队列:- 第一个执行的是打印
1的微任务 → 输出1 - 第二个执行的是打印
2的微任务 → 输出2
- 第一个执行的是打印
第一个用await的代码逻辑同理
a()执行时,外层Promise的resolve()先被调用,但await a()对应的后续代码(打印2),是在a()完全执行结束后,才会被包装成微任务加入队列的。- 而内层Promise的打印
1的微任务,是在a()的执行过程中就已经被加入队列了,所以队列顺序依然是1在前,2在后。
再总结你的核心误解
你之前错误地认为“外层Promise的resolve()会立刻把它的then回调加入微任务队列”,但正确的机制是:
- 只有当你调用
then()注册回调时,如果Promise已经处于fulfilled/rejected状态,才会把该回调加入微任务队列; - 如果先调用
resolve(),再调用then(),那么then()的注册时机才是微任务被添加的时机; - 回到你的代码,内层的
then()是在外层Promise的executor内部就完成注册的,所以它的微任务先被加入队列;而外层的then()(或await的后续逻辑)是在executor执行完之后才注册的,所以微任务排在后面。
这样就完全解释了为什么结果是1然后2啦~
备注:内容来源于stack exchange,提问作者Zexuan Chen
相关产品推荐
相关产品推荐

