剛開始學習JavaScript的時候,很單純的認為所有程式碼是逐行執行的,就像看書不都是ㄧ行一行閱讀嗎?直到踩到callback function 的地雷才意識到必須瞭解 JavaScript 具有非同步特性。
JavaScript 是單線程語言,代表程式碼只能一行一行執行,若上一行的程式碼卡住,下一行的程式碼就無法執行。
但現代網頁的互動特性有些要等待使用者點擊,有些時候則是需要載入第三方提供的影像等等,若是維持同步方式處理的話,當等待資源的時間過長就會造成畫面卡住的現象,因此,JavaScript的設計者將所有要執行的任務分成兩種:
圖片來源:sessionstack
JavaScript 執行環境中有一個叫做 Call Stack 的地方,負責存放要執行的函式。
可以把 Call Stack 想成辦公桌上放待辦事項的地方,每天上班時,可能需要先開會,然後會議上老闆交派許多任務,回到辦公室後得將任務一一分類,有些任務可能需要經過其他部門審核,有些可能需要詢問外部廠商,這些無法由你自己立刻處理的任務,就會給相關部門處理,完成後再放到一個叫做Callback Queue的位置。
而 Event Loop 就像一位積極的秘書,會不斷觀察辦公桌上的代辦事項完成了沒,當辦公桌上沒有任何任務後,就會把放在 Callback Queue 中的任務放到辦公桌上讓你接手完成。
回到程式語言來談,代表當程式碼是瀏覽器提供的API時,就會丟到 Web APIs中。
像是setTimeout就會在到達設定的秒數後被放入CallBack Queue中等待,等到 Call Stack 清空後,Event Loop 就會把 setTimeout 放入Call Stack 並立即執行其中的程式碼。
stack 按照後進先出(LIFO, Last In First Out)的原理運作
quene 運作方式則是先進先出(FIFO, First-In-First-Out)
console.log("決定鐵人賽主題");
setTimeout(() => {
console.log("先找一下資料");
}, 0);
setTimeout(() => {
console.log("開始寫鐵人賽文章");
}, 0);
console.log("完成鐵人賽文章了!");
深入認識了 Event loop 後就可以理解為什麼 setTimeout 明明設定了0秒,為什麼還是不會依序印出:
決定鐵人賽主題 -> 先找一下資料 -> 開始寫鐵人賽文章 -> 完成鐵人賽文章了!
只要記得所有非同步的事件都會先進到 Callback Queue 中,等待 Call Stack 中的任務執行完畢,才會輪到非同步的程式碼執行。
參考資料:
Concurrency model and the event loop
所以說event loop到底是什麼玩意兒?
loupe
理解 JavaScript 中的事件循環、堆疊、佇列和併發模式