setImmediate 总在 Promise.then 之后执行,因其属于 check 阶段宏任务,而 Promise.then 是本轮事件循环末尾清空的微任务;同步代码→nextTick→Promise微任务→poll→check(setImmediate)。

setImmediate 不在微任务队列里,它根本不会和 Promise.then 或 process.nextTick 竞争执行权。它的回调被放进事件循环的 check 阶段队列,而微任务是在每个阶段结束时立刻清空的——所以微任务总在 setImmediate 之前跑完。
微任务清空后才轮到 check 阶段
无论当前处于哪个事件循环阶段(比如刚执行完一个 I/O 回调),只要该阶段结束,Node.js 就会立即执行所有待处理的微任务(包括 Promise.then、catch、finally 回调),直到微任务队列为空。这时才会推进到下一阶段——如果上一阶段是 poll,且已确认无更多 I/O 回调可执行,事件循环就进入 check 阶段,setImmediate 回调才开始执行。
- 同步代码执行完 → 立即清空 nextTick 队列 → 再清空 Promise 微任务队列
- 微任务全部执行完毕 → 进入 poll 阶段(处理 I/O)→ poll 空闲或耗尽 → 进入 check 阶段 → 执行 setImmediate
- 哪怕你在 Promise.then 里立刻调用 setImmediate,那个回调也得等当前 poll 阶段彻底退出后才排队入场
为什么 setImmediate 总比 Promise.then 晚
这不是“慢一点”,而是机制隔离:Promise.then 是微任务,属于“本轮收尾工作”;setImmediate 是宏任务,属于“下一轮的开头环节”。它们不在同一调度层,不存在竞争关系,只有先后依赖。
Miller (mlr) 是一个命令行工具,用于查询、整形和重新格式化名称索引数据,如 CSV、TSV、JSON 和 JSON Lines。它将 awk、sed、cut、join 和 sort 的功能整合到一个专为结构化数据处理而构建的单一工具中。
- 微任务没有阶段归属,只认“当前操作结束”这个触发点
- setImmediate 明确绑定 check 阶段,必须等 poll 阶段主动让出控制权
- 即使 poll 阶段没做任何事(比如空转),也要先走完它,才能进 check
实际执行顺序的锚定点
以一段典型代码为例:
console.log('start');
Promise.resolve().then(() => console.log('then'));
setImmediate(() => console.log('immediate'));
console.log('end');
输出一定是:start → end → then → immediate。因为“start/end”是同步,“then”在本轮末尾清空,“immediate”要等到下一轮 check 阶段才启动。
- process.nextTick 比 Promise.then 更早,在同步代码结束后、微任务之前就执行
- setTimeout(fn, 0) 在 timers 阶段,可能和 setImmediate 交错,但两者都晚于所有微任务
- 所有 setImmediate 调用都登记在同一 check 队列,每轮最多执行一个,不累积也不插队

















