事件循环(Event Loop)机制对比:Node.js vs 浏览器
1. 共同点:基本事件循环模型
两者都基于 "任务队列 + 循环处理" 的机制:
- 主线程执行同步代码。
- 异步任务(如I/O、定时器)完成后,回调函数被放入任务队列。
- 事件循环不断检查队列,按规则取出任务执行。
2. 核心区别
(1)任务队列类型不同
浏览器 | Node.js |
---|---|
宏任务(Macrotask): • script (整体代码)• setTimeout /setInterval • DOM事件回调(如点击) • requestAnimationFrame | 宏任务(Timers/Poll/Check): • setTimeout /setInterval (Timers)• I/O回调(Poll) • setImmediate (Check) |
微任务(Microtask): • Promise.then /catch /finally • MutationObserver • queueMicrotask | 微任务(Microtask): • Promise.then /catch /finally • process.nextTick (优先级最高) |
(2)执行顺序差异
浏览器的循环流程:
- 执行 一个宏任务(如
script
)。 - 清空 所有微任务队列。
- 渲染页面(如有需要)。
- 重复上述步骤。
Node.js 的循环阶段(分6个阶段):
- Timers:执行
setTimeout
/setInterval
回调。 - Pending I/O:处理系统操作(如TCP错误)。
- Idle/Prepare:内部使用(可忽略)。
- Poll:
- 执行I/O回调(如文件读取)。
- 如果队列为空,会等待新I/O事件或检查Timers。
- Check:执行
setImmediate
回调。 - Close:处理关闭事件(如
socket.on('close')
)。
每次阶段切换前:
- 先清空
process.nextTick
队列(优先级最高)。 - 再清空微任务队列(如
Promise
)。
3. 关键差异点
特性 | 浏览器 | Node.js |
---|---|---|
微任务优先级 | Promise 和 MutationObserver 同级 | process.nextTick > Promise |
定时器精度 | 受页面渲染影响(最小4ms) | 更高精度(依赖系统时钟) |
特有API | requestAnimationFrame | setImmediate 、process.nextTick |
I/O处理阶段 | 无明确阶段 | 集中在 Poll 阶段 |
4. 代码示例对比
浏览器:
setTimeout(() => console.log('宏任务1'), 0); Promise.resolve().then(() => console.log('微任务1')); // 输出顺序:微任务1 → 宏任务1
Node.js:
setTimeout(() => console.log('Timers阶段'), 0); setImmediate(() => console.log('Check阶段')); process.nextTick(() => console.log('nextTick')); Promise.resolve().then(() => console.log('Promise微任务')); // 可能输出顺序: // nextTick → Promise微任务 → Timers阶段 → Check阶段
5. 总结口诀
浏览器:
宏微交替,渲染插队,RAF动画优先。
Node.js:
六阶段转,nextTick先,Poll等I/O,Immediate断。
简单记:
- 浏览器:宏 → 微 → 渲染(循环)。
- Node.js:阶段跳,nextTick 总插队。
理解差异能避免异步代码的预期错误!
事件循环机制比喻:Node.js vs 浏览器
1. 共同点:快餐店取餐模式
- 共同流程:
都像一家快餐店(主线程),顾客(任务)点餐后:- 同步任务:直接取餐(立即执行)。
- 异步任务:拿号排队(放入任务队列),服务员(事件循环)按规则叫号处理。
2. 核心区别比喻
(1)任务队列类型不同
浏览器 | Node.js |
---|---|
宏任务 = 普通顾客(setTimeout 、点击事件)微任务 = VIP顾客( Promise ,优先插队) | 宏任务 = 分时段顾客(Timers 、I/O 、Check )微任务 = 超级VIP( process.nextTick 比Promise 更优先) |
(2)执行顺序差异
浏览器流程:
- 叫一个普通顾客(执行一个宏任务)。
- 立刻服务所有VIP(清空微任务队列)。
- 刷新菜单(渲染页面)。
- 重复流程。
Node.js流程(6阶段厨房):
- Timers阶段:处理预约的定时器顾客(
setTimeout
)。 - Poll阶段:盯着取餐口(I/O回调),没人就等新订单。
- Check阶段:立刻处理"现做餐"顾客(
setImmediate
)。 - 每次切换阶段前:
- 先服务老板亲戚(
process.nextTick
)。 - 再服务VIP(
Promise
)。
- 先服务老板亲戚(
3. 关键差异场景比喻
场景 | 浏览器 | Node.js |
---|---|---|
插队优先级 | VIP(Promise )和普通VIP(MutationObserver )平等 | 老板亲戚(nextTick )> VIP(Promise ) |
定时器精度 | 受厨房忙乱影响(最小延迟4ms) | 精准预约(系统时钟控制) |
特殊顾客 | 动画师(requestAnimationFrame ) | 现做餐专员(setImmediate ) |
I/O处理 | 无固定窗口,随机处理 | 专用取餐口(Poll阶段集中处理) |
4. 代码示例对比
浏览器:
// 比喻:普通顾客和VIP点餐 setTimeout(() => console.log('普通顾客'), 0); // 宏任务 Promise.resolve().then(() => console.log('VIP')); // 微任务 // 输出:VIP → 普通顾客 (VIP优先)
Node.js:
// 比喻:分时段+超级VIP插队 setTimeout(() => console.log('预约顾客'), 0); // Timers阶段 setImmediate(() => console.log('现做餐顾客')); // Check阶段 process.nextTick(() => console.log('老板亲戚')); // 最高优先级 // 输出:老板亲戚 → 预约顾客 → 现做餐顾客
5. 终极口诀
浏览器:
一宏一微一渲染,VIP永远要优先。
Node.js:
六阶段轮转不停,老板亲戚赛VIP,Poll阶段等I/O,Immediate断后行。
一句话记:
- 浏览器:宏 → 微 → 渲染(循环)。
- Node.js:阶段跳,nextTick 永远最先到!
通过快餐店比喻,轻松理解两者差异!