Node.js事件循环执行顺序疑问:process.nextTick与Promise的执行差异
案例2的执行顺序与Node.js事件循环机制解析
首先直接给出案例2的运行结果:d, a, c, b
先看调整后的案例2代码:
// case 2 const test = async () => { new Promise((resolve) => { resolve('a'); }).then(r => console.log(r)); console.log(await 'd'); process.nextTick(() => { console.log('c'); }); return 'b'; }; (async () => { const r = await test(); console.log(r); })();
分步拆解执行流程
我们一步步捋清楚代码的执行逻辑:
- 外层立即执行的async函数启动,调用
test()进入该async函数。 - 同步创建一个已resolve的Promise,它的
then回调(打印'a')被加入Promise微任务队列。 - 执行
console.log(await 'd'):await 'd'会先把字符串'd'包装成一个已resolve的Promise,然后暂停test函数,把await之后的所有代码(注册nextTick回调、return 'b')放到这个Promise的then回调里,加入Promise微任务队列。
- 此时
test函数暂停,外层async函数也因为await test()进入暂停,回到事件循环。 - 事件循环开始处理微任务:
- 先检查
process.nextTick队列:此时为空,跳过。 - 处理Promise微任务队列:
- 第一个任务:执行打印'a'的回调。
- 第二个任务:执行
await 'd'对应的回调——先同步打印'd',接着注册process.nextTick回调(打印'c')到nextTick队列,最后让test函数返回的Promise resolve为'b'。
- 先检查
- Promise微任务队列处理完后,事件循环会再次检查微任务:优先处理
process.nextTick队列,执行回调打印'c'。 - nextTick队列清空后,处理最后一个Promise微任务:外层async函数拿到
test返回的'b',打印出来。
背后的Node.js事件循环微任务机制
Node.js的事件循环中,微任务被分为两个优先级不同的队列:
process.nextTick队列:这是优先级最高的微任务队列,每次事件循环阶段结束后,会先彻底清空这个队列(包括执行过程中新加入的nextTick任务),才会处理其他微任务。可以把它理解成“插队”的微任务。- Promise微任务队列:包含Promise的
then/catch/finally回调、async函数中await后续的代码、queueMicrotask创建的任务等,优先级低于process.nextTick队列。
事件循环处理微任务的核心顺序是:
同步代码执行完毕 → 清空所有
process.nextTick任务 → 清空所有Promise微任务 → 进入下一个事件循环阶段
案例1与案例2的差异对比
- 案例1中,
process.nextTick是在同步代码阶段注册的,所以同步代码执行完后,事件循环先处理nextTick任务(打印'c'),再处理Promise微任务(打印'a'、'd'),最后打印'b',得到c,a,d,b的结果。 - 案例2中,
process.nextTick是在Promise微任务的执行过程中注册的,所以当前Promise微任务处理完后,事件循环会先处理刚加入的nextTick任务(打印'c'),再处理外层async函数的Promise微任务(打印'b'),最终得到d,a,c,b的结果。
内容的提问来源于stack exchange,提问作者cody
相关产品推荐
相关产品推荐

