Event Loop


1. Javascript 是 單執行緒 的程式語言 ~ 
單執行緒 定義 : 執行緒是比程序更小的單元,它是cpu的最小執行單元(當然,現在還有協程)。是作業系統能夠進行運算排程的最小單位。一個程序,至少包含一個或多個執行緒。 

單執行緒的程序,有一個特性就是順序執行,當遇到比較耗時的任務時,還未執行的任務就會處於等待狀態,一定要等到前面的任務完成了,才會往後執行。

不過要注意的是,雖然 javascript 本身是單執行緒,但是瀏覽器本身不是,因為瀏覽器上不是只有 javascript



2.堆疊(stack)
在 JavaScript 中的執行堆疊(called stack)會記錄目前執行到程式的哪個部分,如果進入了某一個函式(step into),便把這個函式添加到堆疊(stack)當中的最上方;如果在函式中執行了 return ,則會將此函式從堆疊(stack)的最上方中抽離(pop off)。
以下面的程式碼為例:
例如 




呼叫 printSquare(4)  ; 而printSquare函式中  又有呼叫 squared ; 
而此時  執行的   堆疊(called stack) 會先放入   printSquare(4)   而後 再放入 squared  
後續再呼叫的函式也會在往上堆疊  ; 特性為 先進後出




3. Callback queue
queue 中文翻譯為佇列,是資料結構的一種,它就像排隊一樣,特性為先進先出
進到這邊的函式會等待 Call Stack 清空後才依序將其放回 Call Stack 執行,看名字就知道這邊是一個 Queue,也就是 Web Api 執行完成後,先進入這個 Queue 的函式會先被放入 Call Stack


4. 同步 與非同步 
基本上JavaScript在執行程式的時候是synchronous的,也就是一次只會執行一列,逐行執行,那麼我們會進一步好奇,如果程式是逐行執行,那為什麼它可以監控瀏覽器的一些事件(event)呢?像是偵測滑鼠的點擊、滑動等等這類的非同步呼叫(asynchronous callback)。

首先我們不能忽略的一點是,JavaScript Engine只是在瀏覽網頁過程中的其中一個部分,另外還包含許多其它的部分,像是rendering engine和http request,也就是說,整個網頁在執行的過程中可以是非同步(asynchronous)的,但是單就JavaScript Engine來說,它還是同步(synchronous)逐行執行的。


5. 非同步處理與堆疊(Async Callback & Call Stack)

為了要解決阻塞的問題,我們可以透過非同步(Asynchronous)的方式搭配 callback ,在這裡我們以 setTimeout 來模擬非同步請求的進行,以下面的程式碼為例:

console.log('hi')

setTimeout(function () {
  console.log('there')
}, 5000)

console.log('JSConfEU')


在執行這段程式的時候,執行堆疊(call stack)中會先執行 hi, 接著執行 setTimeout,但是在 setTimeout 中的這個回呼函式(callback function,簡稱 cb)並不會立即被執行(等等會說明它到哪去了),最後堆疊中(stack)會在執行 JSConfEU:


6.Concurrency and Event Loop

event loop 的作用是去監控堆疊(call stack)和工作佇列(task queue),當堆疊當中沒有執行項目的時候,便把佇列中的內容拉到堆疊中去執行。

為了要理解 JavaScript 之所以能夠透過非同步的方式(asynchronous)「看起來」一次處理很多事情,我們需要進一步瞭解 Event Loop。
我們之所以可以在瀏覽器中同時(concurrently)處理多個事情,是因為瀏覽器並非只是一個 JavaScript Runtime。

JavaScript 的執行時期(Runtime)一次只能做一件事,但瀏覽器提供了更多不同的 API 讓我們使用,進而讓我們可以透過 event loop 搭配非同步的方式同時處理多個事項。


在下面的影片中,我們可以看到當我們在堆疊中執行 setTimeout 這個 function 時,setTimeout 實際上是一個瀏覽器提供的 API ,而不是 JS 引擎本身的功能;於是瀏覽器提供一個計時器給我們使用, setTimeout 中的 callback function(簡稱 cb)會被放到 WebAPIs 中,這時候,setTimeout 這個 function 就已經執行結束,並從堆疊中脫離。
當計時器的時間到時,會把要執行的 cb 放到一個叫做工作佇列(task queue)的地方。
這時候就輪到事件循環(event loop)的功能,它的作用很簡單—如果堆疊(stack)是空的,它便把佇列(queue)中的第一個項目放到堆疊當中;堆疊(stack)便會去執行這個項目。
以範例來看,步驟則會是:

1. 執行第一行的 console Hi



2. setTimeout 開始執行倒數 5 秒




3. setTimeout 跳出 Stack,繼續執行下一行,印出 console JSConfEU,完成後跳出 Stack



4. setTimeout 時間到了,把要觸發的 callback function 丟進 task queue 裡面


5. Event Loop 發現 Stack 空了,把 callback function 丟進 stack 去執行



6. 執行 callback function 內容,印出 there,印完後跳出 Stack


以上就是整個 Event Loop 的流程了,並 setTimeout 有幾個重點需要注意:
如果把 setTimeout 的等待時間改為 0,結果也是一樣的
以剛剛的範例來看,其實等待了幾秒不是重點,setTimeout 處理完之後,都會把結果丟進 task queue,他都會等到 Stack 已經清空了才執行。
setTimeout 的等待時間並不代表一定會執行的時間,而是代表「最少」會等待的時間



留言