Node.js 概念
事件驱动、非阻塞 I/O、单线程——这三个词到底在说什么。
Key PointsNode 用单线程 + 事件循环处理并发;耗时的 I/O 交给系统内核,回调排队等结果。
三个关键词
- 单线程 —— 你的 JavaScript 只有一条执行线程
- 非阻塞 I/O —— 发起读文件后立刻返回,不等结果
- 事件驱动 —— 结果就绪时触发回调,事件循环去执行它
Together they mean ten thousand concurrent connections cost one thread's memory, while the classic thread-per-connection model needs ten thousand threads. That is where Node wins on long-lived connections.
阻塞 vs 非阻塞
JavaScript
const fs = require('node:fs');
// 同步:读文件期间整条线程停住
const data = fs.readFileSync('big.txt', 'utf8');
console.log('同步读完', data.length);
// 异步:立刻返回,读完再回调
fs.readFile('big.txt', 'utf8', (err, data) => {
if (err) throw err;
console.log('异步读完', data.length);
});
console.log('这行先执行');Output
同步读完 1048576
这行先执行
异步读完 1048576注意输出的顺序:这行先执行 排在异步回调之前。异步 API 只是发起了读操作,真正的读取由操作系统完成,完成后把回调塞进事件循环的队列。
Node 不适合什么
| Task | Verdict | Reason |
|---|---|---|
| 读 10 万个文件 | 适合 | I/O 密集,内核并行 |
| 压缩 1GB 图片 | 不适合 | CPU 占满,事件循环卡死 |
| 1 万条 WebSocket | 很适合 | 每连接内存开销极低 |
| 矩阵运算 | 不适合 | 没有 SIMD、没有多线程数值库 |
"Single-threaded" refers to your code. libuv's internal thread pool (4 by default) handles files, DNS and crypto, so concurrent I/O is still parallel.
三个关键词
- 单线程 —— 你的 JavaScript 只有一条执行线程
- 非阻塞 I/O —— 发起读文件后立刻返回,不等结果
- 事件驱动 —— 结果就绪时触发回调,事件循环去执行它
三者合起来的效果:一万个并发连接只占一个线程的内存,而传统「一连接一线程」的模型要一万个线程。这就是 Node 在长连接场景下的优势来源。
阻塞 vs 非阻塞
JavaScript
const fs = require('node:fs');
// 同步:读文件期间整条线程停住
const data = fs.readFileSync('big.txt', 'utf8');
console.log('同步读完', data.length);
// 异步:立刻返回,读完再回调
fs.readFile('big.txt', 'utf8', (err, data) => {
if (err) throw err;
console.log('异步读完', data.length);
});
console.log('这行先执行');Output
同步读完 1048576
这行先执行
异步读完 1048576Watch the order: 这行先执行 prints before the async callback. The async API only starts the read; the OS does the work and later pushes the callback into the event-loop queue.
Node 不适合什么
| 任务 | 结论 | 原因 |
|---|---|---|
| 读 10 万个文件 | 适合 | I/O 密集,内核并行 |
| 压缩 1GB 图片 | 不适合 | CPU 占满,事件循环卡死 |
| 1 万条 WebSocket | 很适合 | 每连接内存开销极低 |
| 矩阵运算 | 不适合 | 没有 SIMD、没有多线程数值库 |
「单线程」指的是你的代码。Node 内部的 libuv 线程池(默认 4 个)负责文件、DNS、加密这些操作,所以并发 I/O 依然是并行的。