You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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);
})();

分步拆解执行流程

我们一步步捋清楚代码的执行逻辑:

  1. 外层立即执行的async函数启动,调用test()进入该async函数。
  2. 同步创建一个已resolve的Promise,它的then回调(打印'a')被加入Promise微任务队列。
  3. 执行console.log(await 'd'):
    • await 'd'会先把字符串'd'包装成一个已resolve的Promise,然后暂停test函数,把await之后的所有代码(注册nextTick回调、return 'b')放到这个Promise的then回调里,加入Promise微任务队列。
  4. 此时test函数暂停,外层async函数也因为await test()进入暂停,回到事件循环。
  5. 事件循环开始处理微任务:
    • 先检查process.nextTick队列:此时为空,跳过。
    • 处理Promise微任务队列:
      • 第一个任务:执行打印'a'的回调。
      • 第二个任务:执行await 'd'对应的回调——先同步打印'd',接着注册process.nextTick回调(打印'c')到nextTick队列,最后让test函数返回的Promise resolve为'b'。
  6. Promise微任务队列处理完后,事件循环会再次检查微任务:优先处理process.nextTick队列,执行回调打印'c'。
  7. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 03:43:42