第4章 异步编程与事件循环高频题
异步编程是 Node.js 高并发的基石,面试中约 70% 的题目围绕回调、Promise、async/await 及事件循环的执行顺序。本章从底层机制到实战陷阱,搭建完整回答框架。
高频考点权重: 事件循环(40%)→ Promise 链/错误处理(30%) → async/await 并发控制(20%) → 回调地狱解决(10%)
面试策略: 先用框架“阶段→宏微任务→举例→边界→总结”组织语言,避免想到哪说到哪。遇到复杂输出题,边画边分析更稳。
🔹 问答卡片 1
| 面试问题 | 请解释 Node.js 的事件循环机制,并给出以下代码的输出: |
| 面试官考察点 | 1️⃣ 事件循环各阶段(timers/poll/check)的理解 2️⃣ 宏任务与微任务的执行顺序 3️⃣ nextTick 的特殊优先级 |
| 回答框架 |
|
| 简短示例回答 | 输出顺序:4 → 3 → 2 → 1。首先同步代码 console.log(4);接着 process.nextTick 在微任务中优先级最高,打印 3;然后 Promise.then 打印 2;最后 timer 宏任务执行,打印 1。 |
| 可能追问 | 🔸 如果 setTimeout 和 setImmediate 同时出现,输出顺序取决于什么? 🔸 process.nextTick 和 Promise.then 哪个先执行?为什么? |
| 避坑提醒 | ⚠️ 不要把 process.nextTick 当成微任务,它是“本轮检查队列”,在每次事件循环切换阶段前运行,可能造成 IO 饥饿。 ⚠️ setTimeout(fn,0) 并不严格为 0ms,受系统定时器精度影响。 |
🔹 问答卡片 2
| 面试问题 | 以下 Promise 链输出什么?如果 reject 未被 catch 会怎样? |
| 面试官考察点 | 1️⃣ Promise 链的传递机制 2️⃣ throw 与 reject 的等价性 3️⃣ catch 后是否能继续链式调用 4️⃣ 全局 unhandledRejection 事件 |
| 回答框架 |
|
| 简短示例回答 | 输出:B → C。第一个then抛错,跳过第二个then,被catch捕获打印'B';catch返回undefined,下一个then打印'C'。如果catch缺失,错误会成为未捕获的Promise rejection,建议使用.on('unhandledRejection')处理。 |
| 可能追问 | 🔸 .then(onResolved, onRejected) 与 .catch(fn) 有什么区别? 🔸 async/await 中如何捕获 Promise 的 reject? |
| 避坑提醒 | ⚠️ return 错误对象与 throw 错误不同:return new Error() 会作为成功值传递。 ⚠️ 不要在 Promise 外层使用 try/catch 包裹,需要使用链式 catch 或 await 的方式。 |
🔹 问答卡片 3
| 面试问题 | 如何用 async/await 并发执行多个 HTTP 请求?如果有一个请求失败,如何让其他请求不受影响? |
| 面试官考察点 | 1️⃣ Promise.all 与 Promise.allSettled 的区别 2️⃣ 并发控制与错误隔离 3️⃣ 微任务在 await 内部的执行时机 |
| 回答框架 |
|
| 简短示例回答 | 使用 Promise.allSettled 并发请求,每条请求通过 .catch() 转为默认结果。如果想控制并发数(如限制5个),可用一个for循环每次Promise.all(5个请求),或者使用p-limit库。 |
| 可能追问 | 🔸 await 在事件循环中如何影响微任务执行顺序? 🔸 for 循环中对每个 await 是否会变成串行?如何保持并发? |
| 避坑提醒 | ⚠️ 不要在循环体内直接 await,如 for (const url of urls) { await fetch(url) } 会变成串行,性能低下。⚠️ Promise.all 中任何一个 reject 会导致整体 reject,务必使用 allSettled 或独立 catch。 |
📦 项目案例:高并发 API 网关中的异步流程控制
| 项目名称 | 企业级流量网关服务(中间层) |
| 业务背景 | 为内部微服务提供统一的鉴权、限流、日志、请求聚合功能,前端通过网关转发到后端。网关需要处理大量异步 RPC 调用(如更新缓存、埋点、异步通知)。 |
| 规模指标 | 日均请求 2亿+,峰值 QPS 5000,平均响应时间 < 50ms,涉及 30+ 下游服务。 |
| 技术栈 | Node.js 14+,Express + cluster 模式,Redis 限流,p-limit 控制并发,Winston 日志,Sentry 错误监控。 |
| 核心难点 |
|
| 面试高频追问 |
|
| 可迁移回答话术 | “在网关中,我们使用 p-limit 控制下游并发数,对每个异步请求独立 catch 并降级处理。这样即使某个服务返回 503,主请求仍能返回降级数据。我们还在全局监听 unhandledRejection 和 uncaughtException,确保进程稳定。关键点:别让一个异步错误拖垮整个 Node 进程。” |
面试实战提示: 项目案例中说“我们用了 Promise.allSettled” 比 说“我们用了 try/catch” 更体现技术深度。记得强调降级策略和事件循环的健康度监控。
本章练习建议: 手写 5 道事件循环输出题,结合 SVG 框架逐步分析;然后把 Promise 链错误处理写成代码验证;最后用 Node.js 实现一个带并发控制的异步任务调度器。