JavaScript 是一種單線程的程式語言,簡單的說就是一次只能做一件事,而讓它做到不阻塞的背後功臣就是 Event Loop 這個機制,接下來讓我們一起認識一下它
開始介紹 Event Loop 之前要先認識一下三個東西,分別為 Stack
、Queue
、Web APIs
Stack 中文翻譯為堆疊,是資料結構的一種,它就像是疊盤子一樣,特性為後進先出
Queue 中文翻譯為佇列,是資料結構的一種,它就像排隊一樣,特性為先進先出
Web API 是瀏覽器提供的方法,它並不是 JavaScript 引擎的一部分,且運作於瀏覽器端,也就是說他們可以同時運行,常見的 Web API 有 setTimeout
、XMLHttpRequest
等等,詳細可以看這裡
知道前面一些名詞之後就可以開始來看運作機制了,程式碼運行會經過以上的幾個流程,總共分為三大區域,分別為 Call Stack
、Web Apis
、Callback Queue
這個區塊的運作原理使用 Stack 的方式,程式碼會先到這個區塊執行以下的操作
到這邊的非同步動作會在瀏覽器背景執行,這裡並沒有所謂 Stack 或是 Queue 的概念,而是先執行完成會先丟到 Callback Queue 去做等待
進到這邊的函式會等待 Call Stack 清空後才依序將其放回 Call Stack 執行,看名字就知道這邊是一個 Queue,也就是 Web Api 執行完成後,先進入這個 Queue 的函式會先被放入 Call Stack
Event Loop 指的就是這一整個循環,當 Call Stack 被清空則會檢視 Callback Queue,並將其放入 Call Stack,就這樣不斷的循環,達成一個不阻塞的機制
function A() {
console.log("functionA");
setTimeout(() => console.log("setTimeout1"), 2000);
B();
}
function B() {
console.log("functionB");
setTimeout(() => console.log("setTimeout2"), 1000);
}
console.log("start");
A();
console.log("end");
實際看一下 Demo 來了解運作,以上分別有 A
、B
兩個函式,內部分別還有一個 setTimeout
,而 A
函式會呼叫 B
函式,讓我們來看看 console.log
出來的順序
start
functionA
functionB
end
setTimeout2
setTimeout1
大概流程如下:
console.log("start")
丟入 Call Stack 執行,完成後移出A
函式丟入 Call Stack 執行console.log("functionA")
丟入 Call Stack 執行,完成後移出setTimeout1
丟到 Web Apis
執行B
函式丟入 Call Stack 執行console.log("functionB")
丟入 Call Stack 執行,完成後移出setTimeout2
丟到 Web Apis
執行B
函式執行完成,移出 Call StackA
函式執行完成,移出 Call Stackconsole.log("end")
丟入 Call Stack 執行,完成後移出setTimeout2
執行完成,並將 callback 放入 Callback QueuesetTimeout2
callback 放入 Call Stack 執行console.log("setTimeout2")
執行完畢後移出setTimeout1
執行完成,並將 callback 放入 Callback QueuesetTimeout1
callback 放入 Call Stack 執行console.log("setTimeout1")
執行完畢後移出console.log("start");
setTimeout(() => console.log("setTimeout1"), 1000);
setTimeout(() => console.log("setTimeout2"), 1000);
const endTime = new Date() + 3000;
while (Date() >= endTime) {}
console.log("end");
這個範例執行完兩個 setTimeout
後會跑一個 while
迴圈,讓程式暫停 3 秒,結果如下:
start
end
setTimeout1
setTimeout2
大概流程如下:
console.log("start")
丟入 Call Stack 執行,完成後移出setTimeout1
丟到 Web Apis
執行setTimeout2
丟到 Web Apis
執行while
迴圈setTimeout1
執行完成,並將 callback 放入 Callback QueuesetTimeout2
執行完成,並將 callback 放入 Callback Queuewhile
迴圈達成條件並跳出(3秒)console.log("end")
丟入 Call Stack 執行,完成後移出setTimeout1
callback 放入 Call Stack 執行,完成後移出setTimeout2
callback 放入 Call Stack 執行,完成後移出這裡的重點在於 setTimeout
會在跑完迴圈後直接執行,因為迴圈執行的一秒後 Web Apis
內的 setTimeout
就已經執行完畢並丟到 Callback Queue 等待,所以當迴圈完成、Call Stack 清空後,會觸發 Event Loop 機制,並將 Callback Queue 等待的函式放入 Call Stack 執行
Event Loop 真的是相當有趣的機制,如果要更了解 JavaScript 一定要先了解它,之後在寫一些非同步的運作也會更加容易,那就祝大家學習愉快拉!