Steven's Blog

苟孟的廢文小天地

0%

究竟什麼是event loop?

1
2
3
4
setTimeout(function(){
console.log('delay 0 sec')
}, 0)
console.log('Hello!')

執行結果

在上述的程式碼中,可以發現順序較前的’‘setTimeout’‘卻晚於’‘Hello!’'執行,原因在於JavaScript的event loop。我們先探討JavaScript的幾個特性。

單執行緒

因為JavaScript的處理程序為單執行緒,意思為同一時間僅能處理一件事,要處理完正在處理的程序才能處理下一件事,而當某程序耗費較長的時間時,會導致後面的程序因為該程序而無法繼續進行,也就是所謂的「阻塞」。

JavaScript 引擎

Google’s V8是流行的Javascript引擎之一,它使用在Chrome瀏覽器和Node.js中,V8引擎主要包含兩部分:

  • Memory Heap ( 內存堆 ) — 分配內存將會在這裡發生,以無序的方式儲存任何類型的任意數據
  • Call Stack ( 堆疊 ) — > 引擎跟蹤 code 執行位置的地方,從命名上可知它的資料結構為Stack,資料的進出是以先進後出的方式。

Call Stack ( 堆疊 )

我們來看一段code:

1
2
3
4
5
6
7
8
9
10
11
12
function func1() {
console.log('func1 start')
func2()
console.log('func1 end')
}
function func2() {
console.log('func2 start')
console.log('Hello')
console.log('func2 end')
}

func1()

執行結果:
func result
從以上可以得知,當準備執行func1時,func1被放進了Call Stack,而因為func1呼叫了func2,func2被放到了func1的上方,而當func2執行完畢後(印出func2 end),func1被移出Stack,接著func1執行完畢,換func1被移出Stack,運作過程如下:
call-stack

那所以這和我們最一開始提到的現象有什麼關係呢?
有一些APIs並非是由JavaScript 引擎提供的,像是DOM、AJAX、setTimeout等,這些是瀏覽器提供的,也就是WEB APIs。而JavaScript的call stack遇到這些APIs時,會先丟到網頁讓它處理。

任務佇列(task quene)

而這些網頁處理完的APIs要放到哪呢?
為了避免一處理完丟回call stack造成程序混亂,在處理完WEB APIs後會先丟到task quene的地方,等到call stack處理完後才會傳回task quene進行處理。

事件循環(event loop)

這時候就輪到event loop上場啦,它會一直去查看stack裡面是不是還有東西,如果是空了的話,便會把quene排序第一的任務放到stack裡去執行。

所以在一開始提到的例子中,當javascript引擎遇到setTimeout時,它會判斷它不是JS引擎的功能,於是把它丟到瀏覽器讓瀏覽器處理,接著繼續執行stack接下來的任務,也就是console.log(‘Hello!’);同時瀏覽器也在處理setTimeout,並將處理完的任務放到quene裡,這也是JS所謂的非同步處理(asynchronous)。而當stack空了的時候,event loop便會將quene裡的console.log(‘delay 0 sec’)丟回stack,因此結果才會是先印出"Hello"之後才印出"delay 0 sec"。