Node.js中Promise.resolve已有Promise时的执行交错与延迟问题
Promise解析与PromiseJobs执行机制答疑
代码示例
var line = new Promise( function line_promise(resolve,reject){ resolve( '1' ); } ); line.then((a) => { console.log(' line 1', a); return 2; }).then((a) => { console.log(' line 2', a); return 3; }).then((a) => { console.log(' line 3', a); return 4; }).then((a) => { console.log(' line 4', a); return 5; }).then((a) => { console.log(' line 5', a); return 6; }); var chain = new Promise( function chain_promise(resolve,reject){ resolve( '1' ); } ); chain.then((a) => { console.log(' chain 1', a); return 2; }).then((a) => { console.log(' chain 2', a); return 3; }).then((a) => { console.log(' chain 3', a); return 4; }).then((a) => { console.log(' chain 4', a); return 5; }).then((a) => { console.log(' chain 5', a); return 6; }); var follow = new Promise( function follow_promise(resolve,reject){ resolve( chain ); }); follow.then((a) => { console.log('follow 1', a); return 1; }).then((a) => { console.log('follow 2', a); return 2; }).then((a) => { console.log('follow 3', a); return 3; }); console.log(' line ', line); console.log(' chain ', chain); console.log(' follow ', follow);
执行输出
line Promise { '1' } chain Promise { '1' } follow Promise { <pending> } line 1 1 chain 1 1 line 2 2 chain 2 2 line 3 3 chain 3 3 follow 1 1 line 4 4 chain 4 4 follow 2 1 line 5 5 chain 5 5 follow 3 2
技术疑问解答
1. 当使用已存在的Promise实例resolve一个Promise时,如何理解PromiseJobs的交错执行机制?
PromiseJobs是专门存放Promise回调的微任务队列,事件循环的微任务阶段会依次清空队列,新生成的微任务会追加到队列末尾。
用已完成的chainresolvefollow时,ES规范要求将解析thenable(即chain)的操作包装成一个独立微任务,而非同步完成。结合代码执行流程:
- 同步代码结束后,微任务队列已有
line.then和chain.then的第一轮回调。 - 第一轮微任务执行
line 1,触发下一个line.then回调入队;接着执行chain 1,触发下一个chain.then回调入队。 - 第二轮微任务执行
line 2、chain 2,各自的下一轮回调继续入队。 - 第三轮微任务执行
line 3、chain 3后,解析follow的微任务终于执行:将follow设为resolved,值为chain的最终值1,同时把follow 1的回调入队。 - 后续微任务阶段,
follow的回调会和line、chain剩余的回调交替执行,形成输出里的交错效果。
简言之:resolve Promise实例产生的解析微任务,会插入到当前队列的后续位置,与原有Promise链的回调交替执行。
2. ECMAScript的Promise解析流程为何会导致嵌套链出现交错延迟?
ES规范中,resolve thenable的流程(PromiseResolveThenableJob)不是同步完成的:
- 需调用thenable的
then方法,传入当前Promise的resolve/reject函数。 - 这个调用过程被包装为独立微任务,必须等待当前同步代码和已有微任务执行完毕后才会触发。
在代码中,follow依赖chain,解析follow的微任务要晚于line、chain的前三轮then回调执行。只有当这个解析任务完成,follow才会变为resolved,它的then回调才会被加入队列,因此follow的回调会比line、chain的回调晚几轮执行,形成交错延迟。
本质是:解析thenable的操作本身是一个微任务,它的执行时机滞后于原有Promise链的回调,导致嵌套链的回调被延迟触发。
3. 为何resolve(thenable)相比resolve(value)需要额外的微任务循环轮次?
resolve普通值(如字符串'1')时,Promise状态会同步变为resolved,对应的then回调会立即进入PromiseJobs队列。
但resolve thenable时,必须执行PromiseResolveThenableJob这个微任务:
- 该任务的作用是安全解析thenable:调用其
then方法监听状态变化,再同步当前Promise的状态。 - 这个步骤不能同步执行,因为thenable的
then方法可能包含异步逻辑或副作用,必须放到微任务队列,等待当前同步代码和已有微任务执行完毕后再处理。
回到代码:
line和chain直接resolve普通值,它们的then回调在同步代码结束后直接入队。followresolvechain(thenable),需要先执行解析微任务,完成后follow才变为resolved,它的then回调才会入队。这就比resolve普通值多了至少一轮微任务循环,也就是要等line、chain的前三轮回调执行完,才会触发follow的回调。
额外的微任务轮次,源于解析thenable的过程本身需要一个独立微任务来完成,而非同步执行。
内容的提问来源于stack exchange,提问作者amt
相关产品推荐
相关产品推荐

