1. 程式人生 > >javascript的event loop事件迴圈

javascript的event loop事件迴圈

javascript的event loop事件迴圈

這是今天一個朋友發給我的一個面試題,

感覺還挺有意思的,

寫個部落格以供分享

 

先看看這個面試題目:

觀察下面的程式碼,寫出輸出結果

console.log('0')
setTimeout(function () {  console.log('1');
});

new Promise(function(resolve,reject){
    console.log('2')
    resolve(3)
}).then(function(val){
    console.log(val)
})
console.log(4
)

輸出結果: “0” “2” 4 3 “1”

 

今天主要是分析為什麼輸出結果是這樣的?這就和 javascript 的執行機制密切相關了.

 


 

 

Event QueueEvent Loop

 

javascript 是一門單執行緒的語言, 這就意味著在執行程式碼的時候, 都只有一個主執行緒來處理所有的任務.

我們都知道 javascript 包括同步程式碼和非同步程式碼, 那麼 javascript 是怎麼處理這兩種情況的呢?

 

  • 同步和非同步任務分別進入不同的執行 場所, 同步的進入主執行緒,非同步的進入 Event Table 並註冊函式
  • 當指定的事情完成時, Event Table 會將這個函式(回撥函式)移入 Event Queue
  • 主執行緒內的任務執行完畢為空, 會去 Event Queue 讀取對應的函式,進入主執行緒執行
  • 上述過程會不斷重複, 也就是常說的 Event Loop(事件迴圈)

 

這裡我們引進了 Event Queue 事件佇列這一概念. 所有非同步操作的回撥都會進入到這裡. 然後等到主執行緒空閒, 就會從這裡調取回調執行.

 


 

 

setTimeout

 

setTimeout 相信大家都有使用過, 可以延時執行並且是非同步執行的.

但是有時候我們得到的結果往往是程式碼實際執行的時間比我們想要延時執行的時間要久。這又是為什麼呢?

這就和我們之前所說的 Event Loop 有關了, 我們可以來具體看下 setTimeout 的執行步驟:

 

setTimeout(function () {
    asyncFn()
}, 1000);

syncFn()

 

  • asyncFn 將非同步執行函式放在 Event Table, 並且開始計時
  • 開始執行 syncFn, 但是 syncFn 可能需要處理的內容很多, 執行時間超過 1 秒, 但是計時還在繼續
  • 計時到達 1 秒, setTimeout 延時完成, asyncFn 進入 Event Queue 事件佇列, 但是主執行緒還在執行, 所以只能等待
  • syncFn 執行完成, 此時 asyncFn 從事件佇列中進入主執行緒執行

所以有時候會出現程式碼實際執行時間比延時時間長的情況。

 


 

 

巨集任務和微任務

 

之前我們說過非同步任務會進入到事件佇列中, 不同型別的任務會進入到不同的佇列中, 比如巨集任務會進入到巨集任務佇列中, 微任務會進入到微任務佇列中.

 

我們只要記住 噹噹前執行棧執行完畢時會立刻先處理所有微任務佇列中的事件,然後再去巨集任務佇列中取出一個事件。同一次事件迴圈中,微任務永遠在巨集任務之前執行

這時候我們就可以解釋一開始的程式碼執行結果了:

 

  • 主執行緒執行按順序程式碼
  • 遇到 setTimeout, 回撥進入到巨集任務佇列上
  • 遇到 Promise, 立即執行, then 函式進入到微任務佇列
  • 同步程式碼執行結束, 主執行緒檢查是否存在微任務, 發現 then, 執行
  • 微任務執行完畢, 再去查詢巨集任務 setTimeout, 執行
  • setTimeout 執行結束, 檢查是否存在微任務, 不存在, 結束.

 

 

 

以上。