调用Promise.resolve()处理Promise时的事件循环机制解析——嵌套Promise回调延迟执行原因探究
为什么嵌套Promise.resolve的回调会晚两个tick执行?
先看你给出的代码和输出:
new Promise(resolve => { resolve(new Promise(resolve => resolve(1))) }).then(() => { console.log('tick 3') }).then(() => { console.log('tick 4') }) new Promise(resolve => resolve(1)).then(() => { console.log('tick 1') }).then(() => { console.log('tick 2') })
输出结果:
tick 1 tick 2 tick 3 tick 4
你的疑问核心是:为什么tick 3的回调比tick 1晚了两个“tick”才执行,而不是预期的一个?要搞懂这个,得拆解Promise的内部决议逻辑和事件循环的微任务处理顺序。
关键差异:resolve(值) vs resolve(Promise)
先看第二个Promise链(tick1/tick2那条):
当你用resolve(1)直接决议一个Promise时,这个Promise会立即进入fulfilled状态,它的第一个.then()回调会被直接加入当前的微任务队列,同步代码执行完后就会执行这个回调。
而第一个Promise链的情况完全不同:外层Promise的resolve接收的是另一个Promise对象。这时候,JS引擎不会直接让外层Promise决议,而是会触发一套额外的异步流程——这就是问题的核心。
resolve(Promise)的内部具体操作
根据ECMAScript规范,当你调用Promise的resolve函数并传入一个thenable(比如另一个Promise)时,引擎会做这些事:
- 不会同步完成外层Promise的决议,而是创建一个ResolveThenableJob(一种微任务)。
- 这个微任务的工作是:调用传入的Promise的
.then()方法,把外层Promise的resolve和reject函数作为回调传进去。 - 只有当这个微任务执行时,才会触发外层Promise的决议——如果传入的Promise已经是fulfilled状态,就会调用外层Promise的resolve函数,让它进入fulfilled状态,进而触发它的
.then()回调。
一步步走事件循环
现在我们按事件循环的阶段来拆解执行顺序:
1. 同步代码执行阶段
- 先执行第一个Promise链:
- 创建外层Promise P1,以及内部Promise P2(P2被立即resolve为1,进入fulfilled状态)。
- 调用P1的resolve(P2),引擎创建ResolveThenableJob(任务A),加入微任务队列。
- 给P1注册
.then(tick3),此时P1还是pending,所以回调暂时存在P1的回调列表里,不会入队。
- 再执行第二个Promise链:
- 创建Promise P3,立即resolve为1,进入fulfilled状态。
- 给P3注册
.then(tick1),因为P3已经fulfilled,所以tick1(任务B)被加入微任务队列,排在任务A之后。
此时微任务队列:[任务A, 任务B]
2. 第一轮微任务处理
- 先执行任务B:
tick1,输出tick 1。执行完后,这个回调返回undefined,触发后续的.then(tick2),tick2(任务C)被加入下一轮微任务队列。 - 再执行任务A:ResolveThenableJob,调用P2.then(resolveP1)。因为P2已经fulfilled,所以立即调用resolveP1,让P1进入fulfilled状态。此时P1的
.then(tick3)回调被触发,tick3(任务D)被加入下一轮微任务队列,排在任务C之后。
此时下一轮微任务队列:[任务C, 任务D]
3. 第二轮微任务处理
- 先执行任务C:
tick2,输出tick 2。 - 再执行任务D:
tick3,输出tick 3。执行完后触发后续的.then(tick4),tick4(任务E)加入下一轮微任务队列。
4. 第三轮微任务处理
- 执行任务E:
tick4,输出tick 4。
为什么看起来晚了两个tick?
从执行顺序可以看到:
tick1在第一轮微任务执行,属于第一个“tick”。tick3要等到第二轮微任务的后期才执行,而tick2在第二轮微任务的前期执行。所以从感知上,tick3比tick1晚了两个“tick”周期——中间隔了tick2的执行。
本质就是resolve(Promise)多了一层微任务(ResolveThenableJob),导致外层Promise的决议被推迟了一轮,进而让它的回调排在了第二个Promise链的第二个回调之后。
内容的提问来源于stack exchange,提问作者Lorryntz
相关产品推荐
相关产品推荐

