這篇文章主要是做 所以說 event loop 到底是什麼玩意兒?| Philip Roberts | JSConf EU 這個影片的筆記,這篇文章所放的圖片也是影片擷取出來
JavaScript , What are you ?
JavaScript 是個單執行緒的程式語言,裡面有
Call stack (呼叫堆疊)、Event Loop (事件循環)、Callback queue (回調佇列) 和一些 API 等等
那 V8(Google 開發的開源 JavaScript 引擎)也有 Call stack、Event Loop、Callback queue 和其它 API 嗎?
V8 說我有 Call stack 和 heap,其他是什麼?(疑!!!)
來看一下 JavaScript 的 Runtime, 像是 Chrome 與 Node.js 都使用了 V8 引擎
JavaScript Runtime的簡化示意圖
V8 有 Heap(memory allocation),call stack(execution contexts)
但當你用 setTimeout 或是 DOM 或是 HTTP 請求的東西,那些並不存在在 V8 裡
所以不存在 V8 ,那他們又是怎麼運行的?
那些像是 DOM、AJAX、setTimeout、Event Loop、callback queue 之類的東西,都是 web API ,由瀏覽器所提供額外的東西
就讓我們從頭講起吧
What is the call stack
one thread == one call stack == one thing at a time
Call stack
是執行堆疊,在調用多個函數的腳本中,跟踪其位置的機制。
一旦完成該功能,它將從 stack 的頂部移走,直到沒有剩餘為止。
blowing the stack
也許你有聽過 blowing the stack,像是下面例子
function foo () {
return foo ();
}
foo();
然後 Chrome 會說 :
RangeError: Maximum call stack size exceeded
blocking
blocking 沒有一個嚴格的定義
它其實就是一段很慢的程式
跑起來很慢、同時又在 stack 上的東西就是 blocking
var foo = $.getSync('//foo.com');
var foo = $.getSync('//bar.com');
var foo = $.getSync('//qux.com');
console.log(foo);
console.log(bar);
console.log(qux);
如果上述是同步請求怎辦
那這樣呼叫 $.getSync('//foo.com')
,然後網路請求,等待回應...
好了以後再呼叫 $.getSync('//bar.com')
,然後網路請求,等待回應...
好了以後再呼叫 $.getSync('//qux.com')
,然後網路請求,等待回應...
最後好不容易那三個 blocking 已完成,可以清空 stack,再做 console.log(foo)
、console.log(bar)
、console.log(qux)
在單一執行緒的程式語言並不能像 Ruby 那樣使用多執行緒,我們做了一個網路請求就只能等它跑完為止,因為並沒有處理這種情況的方法
那如果我們有同步請求,那我們就不能做其他事,無法顯示畫面,無法跑其他程式碼都卡住了,但我希望使用者看到流暢 UI ,那我們就不能 block the stack
最簡單的解法就是
asynchronous callbacks
瀏覽器中幾乎沒有 Blocking ,在 Node.js 也是一樣,都是 Async (非同步)
基本上就是執行一些程式碼然後 callback,然後再執行,那實際上是怎樣呢?
console.log('a')
setTimeout(
function cb(){
console.log('b')
}, 1000)
console.log('c')
按照同步程式的執行順序,應該是 a -> b -> c 這個結果,但真正的結果是 a -> c -> b,那到底發生了甚麼事?
畫面不夠清楚可以直接到 loupe 網站來執行看看
loupe 是視覺化的呈現 JavaScript 的 call stack / event loop / callback queue 如何相互影響,有視覺化真的比較好理解
這基本上就是 Event Loop 在 Concurrency 上會發揮作用的地方
就算把上述例子的 setTimeout 變成 0 呢 ? 那執行順序會是如何 ? 結果依然是 a -> c -> b
不要忘了 Event Loop 的特性是必須等到 stack 清空後才可以把 callback 放到 stack ,才繼續執行 console.log('b')
今天就算是執行 AJAX ,它也是會放到 web apis ,在 XHR 等待回應前,或是永遠不會完成也沒關係,stack 都可以順利繼續執行程式碼不會塞住。假設執行完,它就會被放到 task queue,然後再被 Event Loop 抓到 stack 裡。
回顧為何要有 Event Loop ?
一開頭便講了 JavaScript 是個單執行緒的程式語言,一次只能做一件事,你在做其他程式時,他無法做出 AJAX 請求,你在做其他程式時,他無法做出 setTimeout 的請求,讓我們能夠同時做事的原因是 :
瀏覽器不只有 Runtime ,瀏覽器會提供我們其他東西,那些都是你無法取得的執行緒,你只能呼叫它們
而當那些 Web Apis 執行完,它就會被放到 task queue。
之後 stack 被清空後,task queue 裡的事件就會被依序推回 stack。
這個偵測 stack 為空,便把 task queue 內的事件推回給 stack 的機制就稱為 Event Loop。
簡單來說 Event Loop 的工作在於 :
- 查看 stack 和查看 task queue
- 如果 stack 是空的,他就會把位於 task queue 的第一個東西放在 stack,讓它 run
參考資料 :
所以說 event loop 到底是什麼玩意兒?| Philip Roberts | JSConf EU
JavaScript 中的同步與非同步(上):先成為 callback 大師吧!
異步程式設計與事件迴圈
What is an event loop?
Concurrency與Parallelism的不同之處