疑問:JS 單線程,若 `setInterval` 每秒更新時間,同時發起耗時 1 秒以上的 `fetch`,時間顯示會不會卡住?
答案:不會卡住,因為 Event Loop 機制
- Call Stack:同步代碼入棧執行。
- Web APIs:`setTimeout`、`fetch`、`DOM 事件` 由瀏覽器多線程處理,完成後將**回調函數**推入 **Task Queue (Macro-task)** 或 **Micro-task Queue**。
- Event Loop:Call Stack 清空 → 執行 Micro-task (Promise/.then) → 取一個 Macro-task 執行 → 渲染 → 循環。
`setInterval` 回調與 `fetch` 回調都是獨立 Macro-task,**互不阻塞**,交替執行。即使 `fetch` 耗時 2 秒,時間顯示仍每秒準時更新 (誤差 ±幾毫秒)。
真正的多線程:Web Workers
- 適用:大量計算 (圖像處理、加密、解析巨大 JSON) 阻塞主線程導致 UI 卡頓。
- 用法:
// main.js
const worker = new Worker('heavy.js');
worker.postMessage({data: hugeArray});
worker.onmessage = e => console.log('結果:', e.data);
// heavy.js
self.onmessage = e => {
const result = heavyComputation(e.data.data);
self.postMessage(result);
};
- Worker 有獨立 Event Loop、記憶體空間,**無法直接操作 DOM**,透過 `postMessage` 通訊 (結構化複製/Transferable)。
避坑指南
- `setInterval` 若回調執行時間 > 間隔,會堆積任務導致延遲;改用 `setTimeout` 遞歸或 `requestAnimationFrame`。
- 長耗時同步迴圈 (如 `while(true)`) **會徹底凍結頁面**,必須移入 Worker 或拆分為 `setTimeout` 批次。
- `async/await` 本質仍是 Promise + Micro-task,**不改變單線程本質**,只讓非同步代碼寫起來像同步。
結論:**JS 單線程 ≠ 阻塞**。善用 Event Loop 特性 + Web Workers,輕鬆實現高並發非阻塞應用。