第2章 Node.js核心机制与事件循环

Event Loop 是 Node.js 面试的必考点。理解阶段顺序、宏/微任务区分以及常见陷阱是过关关键。本章提供 3 个核心问答 + 1 个真实项目案例,系统掌握事件循环。

步骤1: 梳理阶段 timer → poll → check 步骤2: 分清宏微 宏任务 vs 微任务 步骤3: 陷阱分析 常见坑点举例 事件循环面试回答框架 事件循环常见陷阱 定时器对比 微任务优先级 阻塞主线程 微任务过多 面试官追问路径图

📌 面试问答卡片

Q1:请解释 Node.js 事件循环的各个阶段及其执行顺序。

🧐 考察点:对事件循环底层机制的理解;能否准确说出6个阶段及核心阶段。

🎯 回答框架:先总述循环目的 → 按顺序列举阶段 → 重点讲 timer、poll、check → 指出微任务清空时机。

💡 示例回答:事件循环依次经历 timers、I/O callbacks、idle/prepare、poll、check、close callbacks。poll 阶段等待新 I/O 事件,check 阶段执行 setImmediate。每个阶段结束后清空微任务队列(nextTick 优先于 Promise)。理解顺序是避免异步行为误解的关键。

🔍 可能追问:setTimeout 和 setImmediate 的执行顺序依赖什么?poll 阶段阻塞条件?

⚠️ 避坑提醒:不要遗漏 idle/prepare 和 close callbacks;强调 nextTick 优先于 Promise 微任务。

Q2:宏任务和微任务在事件循环中如何执行?请举例。

🧐 考察点:宏微任务分类;执行队列被清空的时机;典型调度顺序。

🎯 回答框架:定义两类任务 → 说明微任务在每个宏任务结束后清空 → 举例演示顺序。

💡 示例回答:宏任务:setTimeout、setInterval、setImmediate、I/O 回调;微任务:process.nextTick、Promise.then。事件循环每执行一个宏任务,就清空整个微任务队列。例如,timer 阶段执行 setTimeout 回调后,立即运行所有已就绪的 Promise 和 nextTick。

🔍 可能追问:Promise 构造函数中的代码是宏任务还是微任务?nextTick 和 Promise 哪个先执行?

⚠️ 避坑提醒:Promise 构造函数同步执行,.then 才是微任务;nextTick 优先级高于 Promise。

Q3:说说你在 Node.js 开发中遇到的事件循环陷阱及解决方案。

🧐 考察点:实战中阻塞循环的识别能力;对 setImmediate 和 nextTick 使用边界的理解。

🎯 回答框架:列举2-3个陷阱 → 说明产生原因 → 给出解决策略。

💡 示例回答:常见陷阱:setTimeout 与 setImmediate 在 I/O 内顺序颠倒;递归 nextTick 导致 I/O 饥饿;Promise 链中混合微任务打乱顺序。解决方案:使用 setImmediate 拆分大任务,为 nextTick 设置深度限制,用 async/await 控制微任务流量。

🔍 可能追问:如何监控事件循环延迟?长时间任务用 setImmediate 还是 worker_threads?

⚠️ 避坑提醒:setImmediate 本身也是宏任务,不能中断当前宏任务;CPU 密集型任务应使用 Worker Threads。

🏗️ 项目案例:高并发实时消息推送服务

项目名称即时消息推送网关 (IM-Gateway)
业务背景为直播平台提供百万级并发消息推送,要求端到端延迟 < 100ms
规模指标30 万+ 长连接;峰值 5 万 TPS;99 分位延迟 95ms
技术栈Node.js + Socket.IO + Redis + PM2 + Nginx
核心难点广播消息时避免阻塞事件循环;微任务大量堆积导致响应超时
面试高频追问1. 如何防止 setImmediate 造成微任务饥饿?
2. 如何处理大量用户离线重连?
3. 事件循环延迟监控方案?
可迁移回答话术“我们将一次广播拆成多个宏任务:每 100 个连接一批使用 setImmediate 调度,并限制 nextTick 递归深度,从而避免阻塞 poll 阶段。同时用 perf_hooks 定时检测事件循环延迟,超过阈值时主动降级。”
💡 进阶提示:面试中结合真实项目(如消息推送、API 网关)讲述事件循环优化,比纯理论更能体现能力。