快轉到主要內容
  1. Core/

Event Loop 是什麼|JavaScript 非同步的底層機制

Idle Engineer
作者
Idle Engineer
AI Runs. I Nap. | 404 Career Not Found
目錄

TL;DR | 面試情境模擬
#

👴 面試官:JavaScript 的 Event Loop 是什麼?

🧑‍💻 :JS 是單執行緒,一次只能做一件事。Event Loop 是它處理非同步的機制:同步程式碼在 Call Stack 上執行;setTimeout 等非同步操作的回呼放在 Task Queue;Promise 的 .then 放在 Microtask Queue。Call Stack 清空後,Event Loop 先清空所有 Microtask Queue,再取一個 Task Queue 的任務,如此循環。所以 Promise 的 .then 永遠比 setTimeout 先執行。


比喻:餐廳廚師
#

只有一個廚師(單執行緒):

  • Call Stack:廚師現在正在做的菜(同步執行)
  • Web APIs:計時器、網路請求等在廚師旁邊等待中的準備工作
  • Microtask Queue:貴賓(Promise)點的菜,廚師空了立刻做
  • Task Queue:普通客人(setTimeout)排的號碼牌,貴賓都服務完才輪到

執行環境
#

┌─────────────────────────────────┐
│           Call Stack            │  ← 同步執行
│  [ main() ]                     │
│  [ console.log() ]              │
└─────────────────────────────────┘

┌─────────────────────────────────┐
│           Web APIs              │  ← 瀏覽器/Node.js 提供
│  setTimeout / fetch / DOM Events│
└─────────────────────────────────┘

┌─────────────────────────────────┐
│        Microtask Queue          │  ← Promise.then, queueMicrotask
│  [ then1, then2, ... ]          │
└─────────────────────────────────┘

┌─────────────────────────────────┐
│          Task Queue             │  ← setTimeout, setInterval, I/O
│  [ cb1, cb2, ... ]              │
└─────────────────────────────────┘

Event Loop 規則

  1. Call Stack 清空
  2. 清空所有 Microtask Queue
  3. 取 Task Queue 的第一個任務
  4. 重複

執行順序範例
#

console.log('1')                    // 同步

setTimeout(() => {
  console.log('2')                  // Task Queue
}, 0)

Promise.resolve().then(() => {
  console.log('3')                  // Microtask Queue
})

console.log('4')                    // 同步

// 輸出順序:1, 4, 3, 2

為什麼是 1, 4, 3, 2?
#

1. console.log('1') 進 Stack,執行,輸出 1
2. setTimeout 進 Web APIs,計時 0ms 後把回呼放入 Task Queue
3. Promise.then 把回呼放入 Microtask Queue
4. console.log('4') 進 Stack,執行,輸出 4
5. Stack 清空 → 先清 Microtask Queue → 輸出 3
6. 再取 Task Queue → 輸出 2

async/await 底層
#

async/await 是 Promise 的語法糖:

async function foo() {
  console.log('A')
  await bar()          // 等同於 Promise.then
  console.log('C')     // 這行放進 Microtask Queue
}

console.log('B')

// 輸出:B, A, C

await 之後的程式碼,行為等同於 .then() 的回呼,放入 Microtask Queue 等 Stack 清空才執行。


💡 面試官可能會追問
#

Q1:為什麼 JavaScript 是單執行緒?
#

設計之初是為了操作 DOM,多執行緒並行修改 DOM 會產生複雜的競爭條件問題。單執行緒讓 DOM 操作天然安全。需要 CPU 密集任務可以用 Web Worker(獨立執行緒,不能操作 DOM)。

Q2:Node.js 的 Event Loop 和瀏覽器一樣嗎?
#

結構類似,但 Node.js 的 Event Loop 有更多階段(Timers、I/O callbacks、Idle、Poll、Check、Close callbacks),並且有 setImmediateprocess.nextTick(比 Microtask 更早)的差異。

Q3:setTimeout(fn, 0) 和 Promise.resolve().then(fn) 哪個先執行?
#

Promise.resolve().then(fn) 先執行,因為它在 Microtask Queue;setTimeout 在 Task Queue。無論 delay 設為 0,Task Queue 都在 Microtask Queue 之後處理。