昨天大致了解為什麼我們需要 Event Loop, 為什麼 Event Loop 很重要以及該如何運用它後, 今天我們要繼續來看 Event Loop 更詳細的運作方式和例子。
為什麼會說學網頁開發的你不可不知 Event Loop 呢? 因為 Event Loop 可以說是 born for web browser
!
還記得 JavaScript 單線程
運行嗎? 假設 web broswer 一次只能執行一件任務的時候, 當由第三方(e.g. Facebook,Google...)提供的 API 要在你的網站上使用它時, 而 web broswer 等待回應時, 畫面就處在一個顯示的階段, 呈現出 畫面卡住 (blocking)
的樣子。
Credit: https://www.pinterest.com/pin/842454674026547365/
這時候, 就是 Event Loop 華麗出場的時刻了, 像交通指揮警察來管控各方的車流, 防止交通阻塞stuck/blocking。Event Loop 是要確保每項任務可以順暢的運行, 我們可以把它想像成一個駐守於 web browser 裡的 Event Listener (事件監聽器)
。
Event Loop 的工作: 監控 Call Stack
和 Callback Queue
。如果 Call Stack 為空,Event Loop 將從 queue (隊伍) 中拿取第一個事件並將其推送到 Call Stack,將由 Call Stack 有效地運行此事件任務。
Call Stack 在前面介紹過了, 那麼讓我們來讀讀 Web APIs & Callback Queue
Web APIs:
我們所知道的 APIs, 比如 TimeOut, DOM, AJAX…
之後我們也會提到的 setTimeout()
, 當其內部執行完後, 即會將事件任務交給 Callback Queue 去排隊處理, 實現 Concurrency (並發性)
, 也就是 Multi-thread (多線程)
, 能夠 同時處理多項任務
。
Callback Queue:
與 Call Stack 相反, 它是 FIFO (First In, First Out)
, 先進先出, 像在銀行排隊一樣, 先來的先接受到銀行業務服務。
在 Event Loop 的監控下, callback queue 在 Event Loop 的指示下等待 Call Stack 中的項目執行完畢後, 才會把 callback queue 排隊的任務放到 call stack 中去執行。
set setTimeout(() => {}), 0)
const bar = () => console.log('bar') const baz = () => console.log('baz') const foo = () => { console.log('foo') bar() baz() } foo()
Result:
foo
bar
baz
Credit: https://flaviocopes.com/javascript-event-loop/
單純用文字講述可能會有點抽象, 在看完清楚明瞭的 Event Loop 流程圖後, 應該會比較有概念了! 接下來看一個比較複雜一點的…
Challenge: 每個事件都是一個 function callback
console.log('Hi'); setTimeout(function cb1() { console.log('cb1'); }, 5000); console.log('Bye');
Result:
Hi
Bye
cb1
瀏覽器控制台無任務, call stack 為空
console.log('Hi')
被添加到 call stack
console.log('Hi')
被執行了
console.log('Hi')
從 call stack 被移除了
setTimeout(function cb1() { ... })
被添加到 call stack
setTimeout(function cb1() { ... })
被執行了, 瀏覽器使用了 Web APIs → timer, 負責處理倒數計時
setTimeout(function cb1() { ... })
倒數完成後, 從 call stack 中被移除
console.log('Bye')
被添加到 call stack
console.log('Bye')
被執行了
console.log('Bye')
從 call stack 中被移除
5 秒後,計時器完成倒數並將 cb1
callback 推送到 callback queue
在 Event Loop 的監控下, 它把 cb1
從 callback queue 中拿出, 並放置到 call stack 中
cb1
被執行且添加 console.log('cb1')
到 call stack 中
console.log('cb1')
被執行了
console.log('cb1')
從 call stack 中移除
cb1
從 call stack 中被移除
最後要跟大家力推一個讓你可以清楚觀看操作 Event Loop, Call Stack, Callback Queue, JS 執行環境 (Runtime) 動態流程的學習材料: Loupe by Philip Roberts