Web Development 網站開發 (9) - Javascript 非同步:Event Loop

非同步是 Javascript 裡很重要的一個環節,非同步的意思是你的網站可以在同一時間內執行多項任務。想像如果今天網站一次只能執行一件事,光是處理用戶的一個按鈕點擊事件就會讓整個網站卡住;或是在加載某個圖片或影片內容時,網站無法做其他事只能等到加載完成。

本篇文章的內容跟圖片都大量參考及使用以下連結: www.educative.io/edpresso/what-is-an-event-loop-in-javascript

單線程 Single Thread 卻非同步

在其他的語言裡面,要同時執行多個任務時,通常會使用多線程 Multi-thread 來分別處理不同任務。Javascript 裡有大量的非同步事件但卻又說是單線程,難免聽起來令人困惑。其實在瀏覽器或是其他 Javascript runtime 執行時,背後有著 Call Stack 跟 Event Queue 等資料結構巧妙實現出非同步的現象。

Call Stack

Stack 是一個後進先出的資料結構 (Computer Science 101),把它想成是一個 Array,每次要新增或移除東西的時候都是從最上面(後面)開始。放東西是 Push ,拿東西是 Pop。

Call Stack

Call Stack 是一個用來紀錄程式當下執行到的位置。這個位置指的其實是 Function 呼叫到哪一層。Function call function 是一個堆疊的概念,每當呼叫新的 Function 時,我們會把它記錄在 Call Stack 裡,執行完以後把它拿出 Call Stack,這樣就可以看出現在最上面的 Function 就是我們要回到的位置。

// Call Stack
// 1. Call a 時: stack = [a]
// 2. Call b 時: stack = [a, b]
// 3. Call c 時: stack = [a, b, c]
// 4. Call d 時: stack = [a, b, d]
// 每一個 function 實行完回到上一層時都會把這個 function 的資訊先 pop 掉

function d() {
    console.log('d');
}

function c() { 
    console.log('c')
}

function b() {
    c();
    d();
}

function a() {
    b();
}

a();

Event Queue

Queue 是一個先進先出的資料結構 (Computer Science 101),把它想成是一個 Array,每次要新增跟移除東西時分別是從頭跟尾的地方。

Event Queue

這個 Event Queue 的主要目的就是存放我們非同步事件裡待執行的 Functions/Events。用 Queue 的目的就是要讓最先被丟進去的待執行任務先被執行。

Event Loop

達成非同步的效果其實就是一直重複檢查並執行 Call Stack 跟 Event Queue 裡的任務。

在每一次的 Event Loop 裡,先去完成 Call Stack 裡的任務。當過程中有執行非同步的任務像是 setTimeout、fetch 等,這些是瀏覽器內建的函式也被稱作 Browser API,我們會在這些非同步任務執行後的 Callback 任務放進 Event Queue 裡。 Queue 裡的第一個任務會在下一次的 Loop 開始前被移至 Call Stack 。

// 雖然把 timer 設成 0 秒,setTimeout 裡的任務會立即執行,
// 但以下結果還會是先印 end 再印 async event
// 由上圖邏輯看來:
// console.log('end') 會在第一次 loop 就被執行,
// 而 console.log('async event') 會被丟進 Event Queue 等下個回合執行。
setTimeout(function() {
    console.log('async event');
}, 0);
    
console.log('end');
    

License: creativecommons.org/licenses/by-sa/4.0/

Huaying Tsai

Huaying Tsai

擅長 Python, Javascript, React, GraphQL。 想寫寫一些適合新手的程式語言教學文。 想推廣現代社會學習多元技能的風氣,建立了技能交換的平台 - https://thoth.tw
台灣