Event Loop 是 JavaScript 基礎面試最常見的執行順序題。
它不只是背「Promise 比 setTimeout 快」,而是要理解 JavaScript 如何在單一主執行緒上協調同步程式、非同步結果與畫面更新。
JavaScript 是單執行緒,為什麼能做非同步?
JavaScript 一次只會在 Call Stack 執行一段程式。
計時器、網路請求、DOM event 等能力由瀏覽器環境處理。完成後,callback 會進入對應 queue,等待 Event Loop 安排回到 Call Stack。
Call Stack
↕
Event Loop
↙ ↘
Microtask Task Queue
Promise setTimeoutMicrotask 和 Macrotask
常見 microtask:
Promise.then / catch / finallyqueueMicrotaskMutationObserver
常見 task / macrotask:
setTimeoutsetInterval- UI event
- message event
一輪同步程式執行完後,Event Loop 會先清空 microtask queue,再進入下一個 task。
經典執行順序題
console.log("A");
setTimeout(() => {
console.log("B");
}, 0);
Promise.resolve().then(() => {
console.log("C");
});
console.log("D");輸出:
A
D
C
B原因:
- 同步的
A、D先執行 - Promise callback 進 microtask queue
- setTimeout callback 進下一輪 task queue
- 同步程式結束後先清空 microtask,所以
C先於B
setTimeout 0 為什麼不是立刻執行?
0 代表最早可以被排程的延遲,不代表插隊執行。
callback 還是要等待:
- 目前 Call Stack 清空
- microtask queue 清空
- Event Loop 輪到下一個 task
如果主執行緒被長任務卡住,計時器也會延後。
setTimeout(() => console.log("timer"), 0);
const start = Date.now();
while (Date.now() - start < 2000) {
// block main thread
}timer 至少要兩秒後才有機會執行。
async / await 怎麼理解?
await 後面的程式可以理解為接在 Promise microtask 裡繼續執行。
async function run() {
console.log("1");
await Promise.resolve();
console.log("2");
}
console.log("3");
run();
console.log("4");輸出:
3
1
4
2await 前仍是同步執行,await 後則排到 microtask。
Microtask 太多會怎樣?
Event Loop 會先清空 microtask queue。如果程式持續建立 microtask,下一個 task 和畫面更新可能一直沒有機會執行。
function loop() {
queueMicrotask(loop);
}
loop();這可能造成頁面卡住,稱為 microtask starvation。
常見追問
requestAnimationFrame 在哪裡?
requestAnimationFrame 會在瀏覽器準備繪製下一幀前呼叫,適合做和畫面更新同步的動畫,不等同一般 setTimeout task。
fetch callback 是 microtask 嗎?
網路請求由瀏覽器處理;Promise resolve 後,.then callback 會以 microtask 執行。
為什麼長任務會讓畫面卡住?
JavaScript 執行、style/layout、paint 都會競爭主執行緒。同步運算長時間佔住 stack,瀏覽器就無法回應輸入或更新畫面。
面試回答模板
JavaScript 在瀏覽器主執行緒上透過 Event Loop 處理非同步工作。同步程式先在 Call Stack 執行,結束後會先清空 microtask queue,例如 Promise 和 queueMicrotask,再處理下一個 task,例如 setTimeout。setTimeout 0 不是立即執行,只是最早能進入下一輪排程。理解 Event Loop 也能解釋為什麼長任務會阻塞互動和畫面更新。