Event Loop
Event Loop
在了解Event Loop之前須先知曉一些JavaScript的觀念
單執行緒(Single Thread)
JavaScript程式的特性是單執行緒,也就是同一時間只能做一件事,而且一次只會執行一個程式碼片段(one thing at a time、EC)。
Call Stack
由於JavaScript一次只做一件事,因此透過Call Stack來紀錄目前程式執行到哪個部分,當執行到某一個函式時,便將此函式push
到stack; 而當從一個函式中返回時,便將此函式pop
出stack。
function multiply(a, b) {
return a * b
}
function square(n) {
return multiply(n, n)
}
function printSquare(n) {
let squared = square(n)
console.log(squared)
}
printSquare(4)
阻塞(blocking)
一般程式碼的執行順序都是由上往下依序執行,一個執行程序完成後才會在接下一個,這就是同步(Synchronous),但是當程式碼需要等待很長一段時間才能結束時,像是處理資料庫查詢的執行程序時,這時其他的操作像是點選按鈕都會像卡住一樣沒有作用,必須等到此處程式return並從stack中離開後才能往下執行,造成阻塞的情形。
var foo = $.getSync('//foo.com')
var bar = $.getSync('//bar.com')
var qux = $.getSync('//qux.com')
console.log(foo)
console.log(bar)
console.log(qux)
Async Callback
為了解決阻塞的問題,可以使用Async callback(非同步回調)函式的做法,來暫緩callback函式的執行,並在之後的某個時間再回到主執行緒執行。
console.log('hi')
setTimeout(function () {
console.log('there')
}, 5000)
console.log('JSConfEU')
在以上的範例中,如果以同步的執行順序應該是hi->there->JSConfEU,但真正的結果是hi->JSConfEU->there,因為當程式執行到setTimeout
這行程式時,會先被移出main thread外面,而等到main thread執行完stack的程式後,在某個時間才會再將此callback函式移回main thread中執行輸出的動作。
Event Loop
JavaScript在Runtime時一次只能做一件事,但瀏覽器提供了更多不同的Web API讓我們使用,使得同時間可以處理許多事情,稱之為並發(Concurrency),而使用Event Loop搭配非同步(Asynchronous)的方式即可達成此效果。下圖是出自Philip Roberts: What the heck is the event loop anyway?|JSConf EU 2014,為Event Loop的概念圖,也可將Code丟到loupe網站測試。
在瀏覽器中程式碼依序執行(heap)時,會將function丟到call stack中執行,此時執行順序是先進後出(FILO)
而當遇到非同步函式像是WebAPI時,main thread會將此函式移到外面執行,原本的stack仍會繼續執行
當WebAPI執行完後根據完成的時間依序將callback移到queue中,等待stack中執行完為空的時,便會將queue的第一個項目移回stack執行,此queue的執行順序是先進先出(FIFO),Event Loop(事件迴圈)便會不斷地在stack和queue之間檢查是否要將任務移回main thread執行
猜猜以下範例結果
function aFunc(value, cb){
setTimeout(cb, 1000, value)
}
function bFunc(value, cb){
setTimeout(cb, 1000, value)
}
aFunc('a', function cbA(value){console.log(value)})
bFunc('b', function cbB(value){console.log(value)})
a
b
Part 2
function aFunc(value, cb){
setTimeout(cb, 1000, value)
}
function bFunc(value, cb){
setTimeout(cb, 900, value)
}
function cbA(value){
console.log(value)
}
function cbB(value){
console.log(value)
}
aFunc('a', cbA)
bFunc('b', cbB)
b
a
//差別是在有時間差
小結
Javascript是單執行緒的,利用Event Loop機制來達到Never blocking(絕不阻塞)。
Ref
- https://eyesofkids.gitbooks.io/javascript-start-from-es6/part4/eventloop.html
- https://pjchender.blogspot.com/2017/08/javascript-learn-event-loop-stack-queue.html
- http://www.ruanyifeng.com/blog/2014/10/event-loop.html
- https://www.youtube.com/watch?v=8aGhZQkoFbQ
- http://cek.io/blog/2015/12/03/event-loop/
- https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/EventLoop
- https://www.udemy.com/the-complete-javascript-course/learn/v4/t/lecture/9939770?start=0
Author Yujay
LastMod 2018-07-01