為什麼要有事件迴圈機制(Event Loop)

Panthon發表於2018-06-23

事件迴圈機制(Event Loop)是全面瞭解javascript程式碼執行順序繞不開的一個重要知識點。雖然許多人知道這個知識點非常重要,但是其實很少有人能夠真正理解它。特別是在ES6正式支援Promise之後,對於新標準中事件迴圈的理解就變得更加重要了。這裡我們不具體講Event Loop(有很多文章說得已經足夠好了),我們只是做個牽引——為什麼需要Event loop和簡單介紹。

在知道事件迴圈機制之前,我們得確信自己掌握了:

  • 執行上下文(Execution context)
  • 函式呼叫棧(call stack)
  • 佇列資料結構(queue)

希望你拋開已有javascript執行機制方面的相關知識,只要知道以上3點知識就好了。

先來看兩個簡單的例子,看看是否能夠得出正確的執行結果。

//demo01
setTimeout(function() {
    console.log(1);
},0);
console.log(2);

for(var i = 0; i < 5; i++) {
    console.log(3);
}

console.log(4);複製程式碼

//demo02
console.log(1);

for(let i = 0; i< 5; i++){
    setTimeout(function() {
        console.log(i)
    },Math.floor(Math.random()*10+1)*100)

}

console.log(3)複製程式碼

(我們假設我們只知道以上3個知識點)很多人在執行之後可能會感到困惑,為什麼即使設定了setTimeout的延遲時間為0,程式碼還是最後執行?再者定時器的時間(隨機設定)優於遇到setTimeout的順序,時間短的就為什麼先執行?

同常情況下,決定程式碼執行順序的是函式呼叫棧(call stack),很顯然這裡的setTimeout中的執行順序已經不是用call stack能夠解釋清除的了,這是為什麼呢?

答案是佇列(queue)。

為什麼要有事件迴圈機制(Event Loop)

javascript的一個特點就是單執行緒,但是很多時候我們仍然需要在不同的時間去執行不同的任務,例如給元素新增點選事件,設定一個定時器,或者發起Ajax請求。因此需要一個非同步機制

來達到這樣的目的,事件迴圈機制也因此而來。

那麼問題來了瀏覽器是如何做到Event Loop這個機制的了?這就需要您自行去查詢資料或是從分發揮您的想象力。

接下來,就簡單介紹一下Event Loop:

每一個JavaScript程式都擁有唯一的事件迴圈,大多數程式碼的執行順序是可以根據函式呼叫棧的規則執行的,而setTimeout/setInterval或者不同的事件繫結中的程式碼,則通過佇列來執行。

setTimeout為任務源,或者任務分發器,由它們將不同的任務分發到不同的任務佇列中去。每一個任務都有對應的任務佇列。

任務佇列又分為巨集任務(macro-task)與微任務(micro-task)兩種,在瀏覽器中(不說node),包括:

  • macro-task:script(整體程式碼)、setTimeout/setInterval、I/O、UI rendering等。
  • micro-task:Promise。

事件迴圈的順序,決定了JavaScript程式碼的執行順序。

它從macro-task中的script開始第一次迴圈。此時全域性上下文進入函式呼叫棧(call stack),直到呼叫棧清空(只剩下全域性上下文,你可以能為這是一個死迴圈,反正很蠢),在這個過程中,如果遇到任務分發器,就會將任務放入對應佇列中去。

第一次迴圈時,macro-task中其實只有script,因此函式呼叫棧清空之後,會直接執行所有的micro-task。當所有可執行的微任務執行完畢之後,就表示第一次事件迴圈已經結束。

第二次迴圈會再次從macro-task開始執行。此時macro-task中的script佇列中已經沒有任務了,但是可能會有其它佇列任務,而micro-task中暫時還沒有任務。此時會先選擇其中一個巨集任務佇列,例如setTimeout,將該佇列中的所有任務全部執行完畢,然後再執行此過程中可能產生的微任務。微任務執行完畢之後,再回過頭來執行其它巨集任務佇列中的任務。依次類推,直到所有巨集任務佇列中的任務都被執行一遍,並且清空了微任務,第二次迴圈就會結束。

如果在第二次迴圈過程中,產生了新的巨集任務佇列,或者之前巨集任務佇列中的任務暫時沒有滿足執行條件,例如延遲時間不夠後者事件沒有觸發,那麼將會繼續同樣的順序重複迴圈。


相關文章