? 1.1 Node.js 的事件循環原理?如何處理異步操作?
面試官您好,我理解事件循環是 Node.js 的異步非阻塞編程核心。
Node.js 構建在 V8 引擎與 libuv 庫之上。雖然 Node.js 是單線程模型,但它通過事件循環(event loop)機制實現了異步 IO 和高并發能力。
🔁 事件循環核心階段(簡略版):
每一輪事件循環分為多個階段,關鍵階段有:
-
timers:執行
setTimeout
、setInterval
的回調; -
pending callbacks:處理某些系統延遲回調;
-
poll:執行 IO 回調,如網絡、磁盤讀取;
-
check:執行
setImmediate()
回調; -
close callbacks:如
socket.on('close')
; -
每個階段結束后,還會處理 microtasks(如
process.nextTick()
、Promise.then()
);
🧠 異步操作如何配合事件循環?
比如我們調用異步文件讀寫:
fs.readFile('a.txt', () => {console.log('讀文件完成');
});
-
任務由 libuv 線程池處理;
-
完成后注冊回調,等待事件循環進入相應階段;
-
在 poll 階段執行對應的回調。
? 1.2 process.nextTick()、setImmediate()、Promise 的執行順序?
這是一個非常容易被問到的陷阱問題,我用執行模型來解釋它的順序。
Node.js 將任務劃分為兩類:
-
Microtasks(微任務):
process.nextTick()
、Promise.then()
; -
Macrotasks(宏任務):
setTimeout
、setImmediate
;
每個事件循環階段后,都會清空微任務隊列。
📌 執行優先級(從高到低):
1. process.nextTick() // Node獨有,優先級最高
2. Promise.then() // 標準微任務
3. setTimeout(fn, 0) // timers 階段
4. setImmediate() // check 階段
🧪 示例代碼:
setTimeout(() => console.log('timeout'), 0);
setImmediate(() => console.log('immediate'));
process.nextTick(() => console.log('nextTick'));
Promise.resolve().then(() => console.log('promise'));
📌 輸出順序:
nextTick
promise
timeout
immediate
補充:過多使用
process.nextTick()
會造成主線程“餓死”(starvation),所以要慎用。
? 1.3 如何避免阻塞主線程?舉例說明
面試官,Node.js 是單線程的,一旦主線程執行了重 CPU 運算,就會阻塞事件循環,影響并發。
🧨 示例:阻塞代碼
// 計算大量斐波那契數
function fibonacci(n) {if (n <= 1) return 1;return fibonacci(n - 1) + fibonacci(n - 2);
}
fibonacci(40); // 卡住主線程
? 避免方法:
? 1. 使用 worker_threads
執行 CPU 密集型任務
const { Worker } = require('worker_threads');
new Worker('./compute.js');
? 2. 拆分任務(分段執行)
function heavyTask() {let count = 0;function loop() {for (let i = 0; i < 10000; i++) count++;if (count < 1e6) setImmediate(loop);}loop();
}
? 3. 借助外部服務(如 Redis 緩存、Nginx 靜態托管)減少計算需求
? 1.4 Node.js 如何實現定時任務?和瀏覽器定時器有區別嗎?
面試官,Node.js 提供和瀏覽器相同 API,但其實現機制完全不同。
📌 相同點:
-
setTimeout(fn, ms)
、setInterval(fn, ms)
可用; -
支持取消:
clearTimeout()
、clearInterval()
;
📌 不同點:
特性 | Node.js(libuv) | 瀏覽器(Web APIs) |
---|---|---|
核心實現 | libuv 的事件循環 | 瀏覽器引擎 + 宿主環境 |
最小時間精度 | 非實時,受事件循環影響 | 一般為 4ms 最小 |
setImmediate() | Node 獨有,check 階段執行 | 無此函數 |
📌 高級定時任務方案(Node 專屬):
-
node-cron
:cron 表達式任務調度; -
Agenda / Bree
:基于數據庫/文件持久化的任務調度; -
Redis 輪詢 + 消息隊列:實現分布式定時觸發;
const cron = require('node-cron');
cron.schedule('0 0 * * *', () => console.log('每天零點執行'));
? 1.5 cluster 模塊的原理及適用場景?如何實現負載均衡?
面試官,
cluster
是 Node.js 提供的多進程擴展機制,用于充分利用多核 CPU 提升吞吐量。
🔧 原理:
-
主進程(Master)通過
cluster.fork()
創建多個 worker 子進程; -
所有 worker 共享同一個 server 端口;
-
實際請求由主進程分發給 worker 處理;
-
每個 worker 都是獨立的 Node 實例,進程隔離、內存獨立;
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;if (cluster.isMaster) {for (let i = 0; i < numCPUs; i++) cluster.fork();
} else {http.createServer((req, res) => {res.end(`Worker ${process.pid} handled request`);}).listen(3000);
}
?? 負載均衡策略:
-
Linux:基于
SO_REUSEPORT
,內核自動均衡分發請求; -
Windows/macOS:Node 內部使用 Round-Robin 分發策略;
-
Node v16+ 支持與
worker_threads
混合使用,提高 CPU 利用率;
? 適用場景:
-
高并發 API 服務(如網關、接口層);
-
IO 與 CPU 混合負載應用;
-
可與
pm2
配合實現進程守護 + 負載均衡;
? 注意事項:
-
不共享內存,通信需用 IPC(如
worker.send()
); -
狀態同步麻煩(需借助 Redis 等中間件);
-
cluster 并不適合細粒度計算,適合粗粒度多請求任務;
? 總結回顧
問題 | 核心關鍵詞 |
---|---|
1.1 | libuv、事件循環、階段、異步 IO |
1.2 | 微任務 vs 宏任務、執行優先級 |
1.3 | 阻塞避免、worker_threads、任務拆分 |
1.4 | Node 定時器機制、cron、高級調度 |
1.5 | cluster 原理、多進程、負載均衡策略 |