Skip to main content

為何把任務加入到任務佇列理這項動作,必須發生在事件迴圈之外?

一、先理解事件迴圈 (Event Loop) 的工作機制

JavaScript 是單執行緒的,也就是說同一時間只能做一件事。
所以它使用「事件迴圈」來管理同步與非同步任務:

整個執行流程大致如下:

  1. 執行 Call Stack(呼叫堆疊)裡的同步程式碼
    這是立即執行的部分,例如變數宣告、函式呼叫。

  2. 當有非同步任務(像 setTimeout、fetch)時
    它們會交由瀏覽器或 Node.js 的「背景系統」處理。

  3. 背景系統完成後,會把對應的回呼函式(callback)
    加入到任務佇列中。

  4. 事件迴圈檢查

    • 如果 Call Stack 空了(表示同步任務都執行完),

    • 就會從任務佇列中取出下一個任務,放進 Call Stack 執行。

這個循環重複進行,形成「事件迴圈」。


二、重點:任務加入任務佇列這個動作,不能發生在事件迴圈裡面

因為——
👉 事件迴圈的角色是「調度(scheduling)」而不是「執行(executing)」任務。

換句話說,事件迴圈只是負責:

  • 檢查哪裡有可執行的任務(stack 空了嗎?queue 有東西嗎?)

  • 從任務佇列取出任務,交回 JS 引擎執行

不會主動產生任務或把任務加入佇列
「加入任務佇列」這件事是由**外部環境(如瀏覽器 API 或 Node.js runtime)**完成的。


三、舉個例子說明:

console.log("1");

setTimeout(() => {
console.log("2");
}, 0);

console.log("3");

執行順序如下:

階段動作負責者
1console.log("1") 直接執行JS 主執行緒
2setTimeout 交給瀏覽器的「Timer 模組」去等待瀏覽器環境
3console.log("3") 執行完畢JS 主執行緒
4Timer 模組倒數結束,通知事件系統:可以把 callback 放進任務佇列瀏覽器系統層(非事件迴圈本身)
5事件迴圈發現 call stack 空了,從任務佇列拿 callback 執行JS 主執行緒透過事件迴圈調度

你可以看到:

  • 把任務放進任務佇列」是由「Timer 模組」完成的,

  • 而不是事件迴圈。


四、如果讓「加入任務佇列」發生在事件迴圈內,會出現什麼問題?

假設事件迴圈在運作時同時負責「加入任務佇列」,
那麼它就會在執行中不斷地中斷自己來插入新任務,導致:

  • 無限遞迴(infinite loop)或 stack overflow

  • 無法保證任務順序

  • 整個事件調度機制崩壞

這樣事件迴圈就會「忙於管理自己」而無法正確處理任務了。


五、總結一句話 🌟

「任務佇列的新增,是由外部系統(例如瀏覽器 API 或 Node.js runtime)在事件迴圈之外完成的;事件迴圈只負責檢查佇列與執行任務,而不負責把任務放進去。」


關鍵概念回顧:

名稱誰負責職責
Call StackJS 引擎執行同步程式
Web APIs / Node APIs瀏覽器或 Node.js runtime處理非同步任務、把結果放進任務佇列
Task Queue / Microtask QueueJS runtime儲存待執行的任務
Event LoopJS runtime檢查 stack 狀態、調度佇列任務