🎁 立即注册,最多可免费使用 30 分钟在线 AI,无需信用卡。

Node.js 面试问题与答案:事件循环、异步、API 与扩展

September 3, 2026
Node.js 面试问题及解答和代码,涵盖事件循环、异步模式、Express、流、内存泄漏、安全性和扩展。
Node.js 面试问题与答案:事件循环、异步、API 与扩展
node js 面试问题
nodejs 面试问题与答案
node 事件循环面试
express 面试问题

Node.js 面试通常从事件循环和异步代码开始,然后延伸到 API、错误处理、流、性能和安全。候选人最常卡住的地方,是把事件循环说成某种魔法。面试官真正想听的,是你能清楚说明什么在哪儿运行、什么会阻塞。

下面是 24 道题及参考答案。代码示例都很短,方便你凭记忆手写出来。

事件循环与异步

1. 什么是 Node.js?

Node.js 是一个基于 Chrome V8 引擎、运行在浏览器之外的 JavaScript 运行时。它用单线程事件循环执行 JavaScript,配合非阻塞 I/O,因此特别擅长处理大量大部分时间都在等待的连接。

2. 事件循环是怎么工作的?

JavaScript 代码跑在单线程上。当它发起一个 I/O 操作,比如读文件或发请求,Node 会把它交给系统或线程池,然后继续往下走。操作完成后,它的回调会被排队,事件循环在调用栈为空时执行它。循环会依次经过几个阶段:timers、pending callbacks、poll(I/O)、check(setImmediate)和 close callbacks。

3. process.nextTick、Promise、setTimeout 和 setImmediate 的执行顺序是什么?

console.log('1 sync');
setTimeout(() => console.log('4 timeout'), 0);
setImmediate(() => console.log('5 immediate'));
Promise.resolve().then(() => console.log('3 promise'));
process.nextTick(() => console.log('2 nextTick'));

同步代码最先执行。然后是 process.nextTick 回调,接着是 promise 微任务,最后才进入事件循环的各个阶段。在主模块里,setTimeout(0) 和 setImmediate 的顺序可能不确定,但在 I/O 回调内部,setImmediate 总是先执行。要准备好解释原因,而不只是背结论。

4. 什么叫“阻塞事件循环”,怎么避免?

一个耗时的同步任务,比如解析超大 JSON、跑一个很大的循环,或者在请求处理函数里用 fs.readFileSync,会卡住所有其他请求,直到它执行完。避免的办法是使用异步 API、把任务拆成小块,或者把 CPU 密集型工作交给 worker 线程或另一个进程。

5. 回调、Promise 和 async/await 有什么区别?

回调是最早的风格,容易层层嵌套。Promise 表示一个未来的值,可以用 then 链式调用。async/await 是建立在 Promise 之上的语法糖,读起来像同步代码,并且可以用 try/catch 处理错误。

6. 怎么并行执行异步任务?

const [user, orders] = await Promise.all([getUser(id), getOrders(id)]);

Promise.all 只要有一个失败就会 reject。Promise.allSettled 会等所有任务结束并报告每个结果。Promise.race 返回最先敲定的那个。如果调用之间相互独立,不要在循环里逐个 await。

7. worker 线程、子进程和 cluster 有什么区别?

worker 线程在同一个进程里用独立线程运行 JavaScript,可以共享内存,适合 CPU 密集型任务。子进程会启动独立的程序。cluster 模块会 fork 出多个应用副本,每个核心一个,共享同一个端口,这样就能利用所有 CPU 来处理请求。

模块与工具链

8. CommonJS 和 ES modules 有什么区别?

CommonJS 用 require 和 module.exports,同步加载。ES modules 用 import 和 export,是 JavaScript 的标准模块方案,还支持顶层 await。Node 两者都支持,具体用哪种由文件扩展名或 package.json 里的 type 字段决定。

9. package.json 里有什么?dependencies 和 devDependencies 有什么区别?

它保存元数据、脚本和依赖。dependencies 是运行时需要的,devDependencies 只在开发和测试时需要。lock 文件记录精确版本,保证每次安装结果一致。

10. 环境配置怎么处理?

用环境变量,通过 process.env 读取,再配一个经过校验的配置模块。不要把密钥提交到仓库里,生产环境要用密钥管理服务。

API 与 Express

11. Express 里的中间件是什么?

中间件是一个能访问请求对象、响应对象和 next 的函数。它按顺序执行,可以修改请求、结束响应,或者把控制权交给下一个中间件。常见用途包括日志、解析请求体、鉴权和错误处理。

app.use((req, res, next) => {
  console.log(req.method, req.url);
  next();
});

12. Express 应用里怎么处理错误?

在异步处理函数里捕获错误,传给 next(err),然后用一个带四个参数 (err, req, res, next) 的错误处理中间件返回统一的响应。在 Express 5 里,处理函数返回的 rejected promise 会自动转发。对于未捕获的异常和未处理的 rejection,应该记录日志并重启进程,而不是在状态未知的情况下继续运行。

13. 如果让你设计一个创建订单的 REST API 端点,你会怎么做?

校验输入、认证用户、检查权限、在数据库事务里创建订单、返回 201 以及新资源和它的位置,用幂等键处理重复请求,并返回带有合适状态码的清晰错误。

14. 用户认证怎么做?

可以用基于 cookie 的 session,也可以用 JWT 之类的 token。密码要用 bcrypt 或 argon2 这类慢哈希算法加密。用 JWT 时,过期时间要短,refresh token 要谨慎使用,不要把敏感数据放进 token 里。

流、文件与性能

15. 什么是流,为什么要使用流?

流以块为单位处理数据,而不是一次性将所有数据加载到内存中。流的类型包括可读流、可写流、双工流和转换流。在处理大文件、上传和下载时使用流。

const fs = require('node:fs');
const { pipeline } = require('node:stream/promises');
const zlib = require('node:zlib');
await pipeline(fs.createReadStream('in.log'), zlib.createGzip(), fs.createWriteStream('in.log.gz'));

16. 什么是背压?

当可写流处理速度跟不上可读流时,它会向数据源发出信号要求减速。pipeline 和 pipe 会为你处理这个问题。如果手动写入,请检查 write() 的返回值,并等待 drain 事件。

17. 如何发现内存泄漏?

持续观察内存变化,采集堆快照(Chrome DevTools 或 --inspect),然后对比快照找出哪些对象在持续增长。常见原因包括永不过期的全局缓存、未移除的事件监听器,以及持有大对象的闭包。

18. 如何对 Node.js 应用进行扩展?

使用 cluster 模块或进程管理器运行多个进程,将它们置于负载均衡器之后,通过将会话移至 Redis 或数据库来保持应用无状态,使用缓存,并用容器进行横向扩展。将 CPU 密集型工作交给 worker 或独立服务处理。

19. 如何对慢接口进行性能分析?

先用日志和计时进行测量,然后使用 --prof 或 --cpu-prof,或火焰图工具来找出热点函数。检查数据库查询,因为瓶颈往往出在那里,而不是 JavaScript 代码中。

安全与可靠性

20. Node.js 常见的安全问题有哪些?

注入攻击(SQL、NoSQL、命令注入)、跨站脚本攻击、认证薄弱、依赖项漏洞、代码中的密钥泄露、缺少速率限制,以及不安全的反序列化。通过输入验证、参数化查询、helmet、依赖审计(npm audit)和最小权限原则来缓解这些问题。

21. 如何实现优雅关闭?

收到 SIGTERM 信号后,停止接受新连接,让正在处理的请求完成,关闭数据库连接,然后退出,并设置超时作为兜底方案。

process.on('SIGTERM', () => {
  server.close(() => process.exit(0));
});

22. 如何测试 Node.js 代码?

使用内置的 node:test、Jest 或 Vitest 等测试运行器进行单元测试,对外部调用进行 mock。针对测试数据库进行集成测试,使用 Supertest 等工具进行 HTTP 层面的测试。保持测试快速且相互独立。

设计与场景

23. 你的 API 在高负载下变慢,如何排查?

检查延迟和 CPU 的监控指标。如果某个核心的 CPU 很高,查找阻塞代码或繁重的 JSON 处理。如果 CPU 不高但延迟很高,则关注数据库或其他服务的等待时间。然后添加缓存、索引、连接池或批处理,再在负载下重新测试。

24. 如何为大文件构建上传接口?

将上传直接流式传输到存储,例如使用预签名 URL 的对象存储,而不是在内存中缓冲。设置大小限制、验证文件类型、按需扫描,之后在后台任务中处理。

如何准备

编写一个小型 API,包含认证、数据库和上传路由,然后用负载工具对其进行性能分析并修复一个问题。练习画出事件循环,并解释像上面这样的小脚本的输出。准备一个关于调试性能或可靠性问题的真实经历。对于编程环节,请参阅我们的编程面试助手指南,对于设计环节,请参阅系统设计指南。

大声说出你的答案,并阅读文字记录,找出你使用了某个术语却没有解释的地方。在任何现场面试中,遵守雇主关于 AI 工具的规定。

用 YesToTheOffer 练习 Node.js 面试答案

进行一次模拟技术面试,大声解释事件循环,然后回顾你的文字记录。30 分钟免费,无需订阅。

iOS & Android — 即将推出
查看价格