浏览器与 Node.js 事件循环的区别
对比浏览器与 Node.js Event Loop 的共同目标、task/rendering 模型、libuv phases、nextTick 与 V8 microtasks。
[!info] related notes
浏览器与 Node.js 事件循环的区别
共同目标
两者都让 JavaScript 在默认单线程、run-to-completion 的执行模型下处理异步 I/O。整个浏览器或 Node.js Runtime 并不是单线程:网络、文件、操作系统、线程池和 Worker 可以在其他执行单元工作;Event Loop 负责让相应 JavaScript callback 在合适时机回来执行。
浏览器:task + microtask checkpoint + rendering
选择一个 task
→ 执行其中的 JavaScript
→ microtask checkpoint
→ 可能 rendering
→ 下一个 task
浏览器必须协调页面更新与用户输入,因此 rendering opportunity 是模型的重要部分。详见 浏览器环境下的 JS Event Loop。
Node.js:libuv phases + nextTick + V8 microtasks
Node.js 没有页面渲染主线,主要面向网络、文件和系统 I/O。用户可见的核心 phases 包括:
pending callbacks
→ idle / prepare
→ poll
→ check
→ close callbacks
→ timers
Node.js 20 / libuv 1.45 起,正常 event-loop iteration 中 timers 在 poll 之后运行;旧资料常画成 timers 位于 poll 前,不应死背为跨版本不变顺序。
poll:等待并处理大部分 I/O events;check:执行setImmediate();close callbacks:执行部分资源关闭回调;timers:执行达到阈值的 timeout / interval callbacks。
process.nextTick() 是额外调度层
process.nextTick() 不属于 libuv phase。实用模型是:
当前 JavaScript callback 返回
→ process.nextTick queue
→ V8 microtasks(Promise / queueMicrotask)
→ 继续 event-loop phase
递归 nextTick 或 microtask 都可能让 I/O / timer 长时间得不到机会。
对比
| 维度 | 浏览器 | Node.js |
|---|---|---|
| 核心场景 | UI、用户事件、网络 | 网络、文件、系统 I/O |
| 主模型 | task + microtask checkpoint + rendering | libuv phases + nextTick + V8 microtasks |
| 页面渲染 | 有 | 无 |
| timer | 后续 task 来源之一 | timers phase / 机制 |
| I/O 中心 | 浏览器宿主调度 | poll phase |
setImmediate | 无 Node 同名语义 | check phase |
process.nextTick | 无 | Node 特有队列 |
面试回答
浏览器和 Node.js 的目标相同,都是在 JavaScript 默认单线程执行的前提下实现非阻塞异步,但调度模型不同。浏览器主要看 task、microtask checkpoint 和 rendering;一个 task 执行完后会清空 microtasks,然后浏览器可能渲染。Node.js 没有页面渲染,而是由 libuv 组织 poll、check、timers、close callbacks 等 phases,并且还有优先于普通 V8 microtasks 处理的
process.nextTickqueue。出现setImmediate、I/O callback 或 nextTick 时,不能只套浏览器的“宏任务/微任务”口诀。
学习检查
不看正文回答:
- 浏览器与 Node.js Event Loop 的共同目标是什么?
- 浏览器为什么必须把 rendering opportunity 纳入调度模型?
- Node.js 的 poll、check、timers 分别承担什么职责?
process.nextTick为什么不能简单归类成浏览器意义上的普通 microtask?- 为什么“宏任务 → 微任务”口诀不足以分析 Node.js 的
setImmediate/ I/O callback? - 递归 nextTick 或 microtask 为什么可能造成 starvation?
- 给一段同时包含 Promise、setTimeout、I/O、setImmediate 的 Node 代码,先说明需要哪些上下文信息,再预测顺序。