setimmediate 在 i/o 回调中总比 settimeout(0) 先执行,因其位于 check 阶段而后者在 timers 阶段;主模块中顺序不确定,因冷启动无 i/o 推动 poll→check 流程;check 阶段专为 i/o 后即时响应设计。

因为 setImmediate 被安排在事件循环的 check 阶段,而 setTimeout(0) 被安排在 timers 阶段;当代码在 I/O 回调(如 fs.readFile、net.connect 的回调)中执行时,事件循环刚完成 poll 阶段,会立即进入 check 阶段处理 setImmediate,之后才回到下一轮循环的 timers 阶段执行 setTimeout —— 这就决定了它“总是更早”。
setImmediate 在 I/O 回调里稳居优先位
Node.js 事件循环在完成一次 I/O 操作后,会把控制权交给 poll 阶段;一旦 poll 队列清空或没有新事件,它不等待,直接跳转到 check 阶段。此时所有已排队的 setImmediate 回调立刻被执行。
- setTimeout(0) 的回调仍留在 timers 队列,要等下一轮事件循环开始时才轮到 timers 阶段
- 哪怕只差一个 tick,I/O 回调内部的 setImmediate 实际上比 setTimeout(0) 少走一整轮循环
- 这是 Node.js 明确设计的行为,不是巧合,也不依赖系统负载
主模块中顺序不确定,但原因很实在
如果 setImmediate 和 setTimeout(0) 都写在最外层(比如 index.js 开头),它们的执行顺序可能颠倒 —— 因为事件循环启动时,timers 阶段可能先被触发,也可能因内部调度微小延迟导致 check 阶段抢先进入。
- setTimeout(0) 实际被强制设为至少 1ms 延迟,受底层计时器精度影响
- 主模块属于“冷启动”,没有前序 I/O 推动 poll→check 流程,两个回调分属不同阶段、无强先后约束
- 这种不确定性正是官方文档强调“不要依赖执行顺序”的原因
check 阶段专为“I/O 后即刻响应”而设
setImmediate 的定位就是填补 I/O 完成与下一轮定时器之间的空档:它不抢占当前同步任务,也不拖到下一轮 timers,而是卡在 poll 结束后的第一时间点执行。
- 适合做清理、状态同步、轻量级后续处理 —— 比 Promise.then() 延迟略高,但比 setTimeout(0) 更贴近 I/O 上下文
- 和 process.nextTick 不同,它不插队当前阶段,因此不会饿死其他宏任务
- 本质上是 Node.js 对“异步但非延迟”这一语义的原生支持
不复杂但容易忽略:执行时机差异来自阶段位置,而非函数名里的“immediate”。真正起作用的是你在哪个上下文中调用它。











