JavaScript 入門: Event Loop 是什麼?為什麼 JS 能非同步?

更新 發佈閱讀 8 分鐘

在前一篇文章中,我們了解到了 Callback、Promise、async / await 等實現非同步操作的方法,但有一個更關鍵的問題:

JavaScript 是單執行緒,為什麼可以非同步?

答案是因為我們有!! 事件循環(Event Loop) !


單執行緒

JavaScript 本身是單執行緒,也就是說 JS 執行程式時:

  • 同一時間只能執行一段同步程式碼
  • 只有一個 Call stack (呼叫堆疊)

在程式運行時,能立刻執行的程式會直接進 Call stack,當 Call stack 清空後,程式才會繼續,所以如果程式中需要長時間運行的任務,就會阻塞其他程式碼執行。


Call Stack 呼叫堆疊

Call stack 是一個追蹤函式的資料結構,從 "stack" 這個名字應該也很好猜測,他是一個後進先出的結構。當函式被呼叫時會放到 stack 頂端,執行完後丟出。


非同步程式的執行步驟

JavaScript 是單執行緒,透過執行環境 (瀏覽器或 Node.js) 的API 可以實現非同步操作。

快速看個例子

console.log("start");

setTimeout(() => {
console.log("timeout");
}, 1000);

console.log("end");

這段程式碼的輸出會是:

start
end
timeout

可以看到 end 會出現在 timeout 之前 ,為什麼會是這樣呢? 為甚麼他們不會按照順序印出呢? 這就是因為有 event loop 悄悄的在運作~🪄

注意:即使 setTimeout() 設定 0 毫秒,也仍會在 end 後才印出
非同步的概念在【JavaScript 入門:實現非同步函式:callback、promise、async/await】有詳細介紹過,如果忘記或是還不熟悉的朋友可以先回去看看唷!

JavaScript 執行環境的組成

不論是在瀏覽器或是 Node.js,JavaScript 的執行環境都包含了:

  1. Call Stack (後進先出)
  2. Web APIs
  3. Task Queue (先進先出)
  4. Event Loop
要注意 API 是由瀏覽器或 Node.js 提供,而非 JS 語言內建的!

非同步程式的執行流程

一樣用剛剛的程式做範例,這邊的 setTimeout 是一個非同步的例子

setTimeout(() => {
console.log("timeout");
}, 1000);

第一步:進入 Call Stack

setTimeout() 進入 call stack

// Call Stack
setTimeout()

第二步:交給 Web API

callback 與延遲 1000ms 工作會被交給 Web API 進行,setTimeout() 立刻返回後從 Call Stack 彈出,同時計時器就在背景進行倒數

// Web APIs
timer (1000ms)

*同一時間,其他程式被擺入 call stack 執行*

第三步:時間到 → 回呼函式進入 Task Queue

當計時器倒數完 1000ms 後,會將 Callback 丟到 Task Queue

//Task Queue:
() => console.log("timeout")

第四步:Event Loop 開始工作

Event loop 會檢查 call stack 空了沒,空了就去 task queue 把任務塞到 call stack 中。

() => console.log("timeout") ​被從 task queue 移到 call stack 執行

第五步:結束!

有了 Web API 與 Event loop 協調執行順序,我們的程式才不會被需要等待的任務卡住。


Event loop 運作機制

簡單來說,Event loop 的工作就是當 Call stack 空了,去 Task queue 找任務再擺到 Call stack 中,但你也知道實際上不像說起來這麼簡單!!

在 JS 中有兩種 Queue

  • Microtask Queue (微任務):promise.then()、catch()、finally()、queueMicrotask
  • Macrotask Queue (宏任務):script(整體程式碼)、setTimeout、setInterval、DOM 事件

Event loop 的運作步驟

  1. 執行第一個宏任務(例如整個 Script)
  2. Call stack 被清空
  3. 清空所有 Microtask queue(包括執行中產生的,也就是說 Microtask 是可以插隊到 Macrotask 前)
  4. 取出一個 Macrotask queue
  5. 重複步驟 2

實戰範例

console.log("start");

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

Promise.resolve().then(()=> console.log("promise"));

console.log("end");

這段程式碼的輸出結果會是什麼呢~~~

答案

start
end
promise
timeout

步驟

  1. 執行第一個宏任務 (整個 script),所以會先印出 "start"
  2. 遇到 setTimeout,交給 web API,回呼函式丟到 Macrotask queue
  3. 遇到 Promise.then(),回呼函式丟到 Microtask queue
  4. 印出 "end",第一個宏任務( script) 結束
  5. Call stack 空了
  6. 清空 Microtask queue,印出 "promise"
  7. 取出第一個 Macrotask queue,印出 "timeout"

總結

JavaScript 本身是單執行緒,靠著執行環境(瀏覽器或 Node)提供的 API 與 Event loop 進行排程,實現非同步操作。Event loop 在 Call stack 清空後從 Queue (microtask 與 macrotask)中取出任務放到 Call stack 執行。

最後總結一下大家都做了些什麼:

  • Callback 解決「結果何時回來」
  • Promise 解決「Callback 的回呼地獄」
  • async/await 解決「可讀性問題」
  • Event loop 解決「非同步任務的排程問題」

參考

  1. 初學者指南:深入了解 JavaScript 的 Call Stack(呼叫堆疊)
  2. 請說明瀏覽器中的事件循環 (Event Loop)
  3. 最常見的事件循環 (Event Loop) 面試題目彙整
留言
avatar-img
Elaine 粼粼的林林總總
7會員
33內容數
不定期地分享程式/旅遊/學習/閱讀或各式各樣的文章,如果對我的分享有興趣,歡迎來找我玩~
2026/02/19
在現代 JavaScript 開發中,「非同步操作」無所不在,如果沒有良好的非同步處理方式,程式碼容易變得混亂難維護。JavaScript 的非同步處理方式經歷了 Callback、Promise、Async / Await。本文將逐步介紹非同步函式的演進過程。
2026/02/19
在現代 JavaScript 開發中,「非同步操作」無所不在,如果沒有良好的非同步處理方式,程式碼容易變得混亂難維護。JavaScript 的非同步處理方式經歷了 Callback、Promise、Async / Await。本文將逐步介紹非同步函式的演進過程。
2026/02/14
本文介紹 JavaScript 中的同步與非同步概念,透過簡單的程式範例說明兩者的執行順序差異,並解釋為什麼 setTimeout 會在主程式執行完後才觸發回呼函式。說明非同步的重要性及它如何避免耗時任務阻塞主執行緒。
2026/02/14
本文介紹 JavaScript 中的同步與非同步概念,透過簡單的程式範例說明兩者的執行順序差異,並解釋為什麼 setTimeout 會在主程式執行完後才觸發回呼函式。說明非同步的重要性及它如何避免耗時任務阻塞主執行緒。
2026/02/10
為什麼說 JavaScript 函式是一等公民 ? 本篇文章透過超簡單白話文範例,帶你理解一級函式、高階函式 (Higher-order function) 與回呼函式 (Callback function) 的定義與關係。學會如何將函式當作參數傳遞與回傳,並為接下來的非同步程式設計打下堅實基礎!
2026/02/10
為什麼說 JavaScript 函式是一等公民 ? 本篇文章透過超簡單白話文範例,帶你理解一級函式、高階函式 (Higher-order function) 與回呼函式 (Callback function) 的定義與關係。學會如何將函式當作參數傳遞與回傳,並為接下來的非同步程式設計打下堅實基礎!
看更多
你可能也想看
Thumbnail
vocus 慶祝推出 App,舉辦 2026 全站慶。推出精選內容與數位商品折扣,訂單免費與紅包抽獎、新註冊會員專屬活動、Boba Boost 贊助抽紅包,以及全站徵文,並邀請你一起來回顧過去的一年, vocus 與創作者共同留下了哪些精彩創作。
Thumbnail
vocus 慶祝推出 App,舉辦 2026 全站慶。推出精選內容與數位商品折扣,訂單免費與紅包抽獎、新註冊會員專屬活動、Boba Boost 贊助抽紅包,以及全站徵文,並邀請你一起來回顧過去的一年, vocus 與創作者共同留下了哪些精彩創作。
Thumbnail
練習目標 這篇是我的前端練習記錄,透過 CodePen 製作一個小功能:「點一下按鈕,就讓整個畫面換個背景色」。 這個練習適合剛開始學習 HTML、CSS、JavaScript 的人,幫助理解: 按鈕怎麼綁定事件 JavaScript 怎麼控制畫面樣式 如何產生隨機顏色 練習畫面預覽
Thumbnail
練習目標 這篇是我的前端練習記錄,透過 CodePen 製作一個小功能:「點一下按鈕,就讓整個畫面換個背景色」。 這個練習適合剛開始學習 HTML、CSS、JavaScript 的人,幫助理解: 按鈕怎麼綁定事件 JavaScript 怎麼控制畫面樣式 如何產生隨機顏色 練習畫面預覽
Thumbnail
這篇文章深入淺出地解釋 JavaScript 中表達式 (expression) 與陳述式 (statement) 的差異,並以 React 中 JSX 的應用為例,說明為何大括號 {} 內只能放入表達式。文章以類比人類語言的句子結構來幫助理解,並提供相關參考資料連結。
Thumbnail
這篇文章深入淺出地解釋 JavaScript 中表達式 (expression) 與陳述式 (statement) 的差異,並以 React 中 JSX 的應用為例,說明為何大括號 {} 內只能放入表達式。文章以類比人類語言的句子結構來幫助理解,並提供相關參考資料連結。
Thumbnail
iFrame在微前端架構中的優缺點,分析了其對用戶體驗、SEO及性能的影響。 iFrame在嵌入小型UI元件、創建隔離環境等特定場景中仍具備相當的應用價值。
Thumbnail
iFrame在微前端架構中的優缺點,分析了其對用戶體驗、SEO及性能的影響。 iFrame在嵌入小型UI元件、創建隔離環境等特定場景中仍具備相當的應用價值。
Thumbnail
本文介紹如何使用Vite建立前端開發初始檔案,並加入Tailwindcss的教學。透過指令和配置檔,讓你能快速建立個人專案的開發環境,並學習如何加入全域的Tailwindcss樣式。還有影片教學、資源連結和更多相關教學文章等,幫助你進一步學習。
Thumbnail
本文介紹如何使用Vite建立前端開發初始檔案,並加入Tailwindcss的教學。透過指令和配置檔,讓你能快速建立個人專案的開發環境,並學習如何加入全域的Tailwindcss樣式。還有影片教學、資源連結和更多相關教學文章等,幫助你進一步學習。
Thumbnail
為什麼要登出使用者? 安全性:防止未經授權的人,在使用者暫離時使用系統,這在公用或共享電腦的環境中尤其重要。 資料保護:只要使用者處於登入狀態,就會暴露在個人資料被他人操縱或利用的風險中,因此登出閒置使用者對資安也很重要。 如何在 Vue 3 專案中實作此功能?
Thumbnail
為什麼要登出使用者? 安全性:防止未經授權的人,在使用者暫離時使用系統,這在公用或共享電腦的環境中尤其重要。 資料保護:只要使用者處於登入狀態,就會暴露在個人資料被他人操縱或利用的風險中,因此登出閒置使用者對資安也很重要。 如何在 Vue 3 專案中實作此功能?
Thumbnail
這一集用最新的Vite工具去創建初始檔案。Vite用於創建和構建Web應用程序,具有快速的啟動時間、即時熱更新、小型體積、支持多種框架和可擴展性等優點。
Thumbnail
這一集用最新的Vite工具去創建初始檔案。Vite用於創建和構建Web應用程序,具有快速的啟動時間、即時熱更新、小型體積、支持多種框架和可擴展性等優點。
Thumbnail
隨著科技發展迅速,軟體職缺需求大增長,有些朋友對IT產業有興趣並想成為一位軟體工程師,但不知道從哪裡下手,透過傳統學校、培訓班或自學等不同方法,有多種學習路徑可以選擇。此外,還提供了一些額外資源教學連結,方便讀者進一步提升相關技能。
Thumbnail
隨著科技發展迅速,軟體職缺需求大增長,有些朋友對IT產業有興趣並想成為一位軟體工程師,但不知道從哪裡下手,透過傳統學校、培訓班或自學等不同方法,有多種學習路徑可以選擇。此外,還提供了一些額外資源教學連結,方便讀者進一步提升相關技能。
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News