1. 程式人生 > >【JS檔案揭祕】第二集 Event loop與執行棧

【JS檔案揭祕】第二集 Event loop與執行棧

  我時常在思考關於JS的很多知識在工作中有什麼用?是否只能存在於面試這種理論性的東西中,對於我們的業務和工作,它們又能扮演怎樣的角色。以後在JS檔案揭祕的每一期裡,都會加入我對於業務的思考,讓這些知識不再是空中樓閣,而是有實際操作的意義。

 

業務場景

  所有的核心在於執行順序,它能幫助我們正確判斷程式碼按照怎樣的順序去執行。避免因為執行順序與預期不一致而導致的bug。如果遇到這種因為執行順序問題而產生的bug,也能通過event loop分析出正確的執行順序,定位bug並解決。

 

關鍵詞解釋

  執行棧:一個專門用來存放執行程式碼的棧記憶體結構,它就像一個容器,也叫做執行的主執行緒。如果遇到函式,會根據回撥關係把回撥層級深的函式先推入到執行棧底部執行,其他語句依次推入,當全部執行完了後,會從上到下依次彈出執行程式碼(也就是“先進後出”),供下一次使用;

  巨集任務(只針對瀏覽器環境):宿主環境提供的任務,包括:script程式碼塊,MessageChannel,requestAnimationFrame,setTimeout,setInterval;

  微任務(只針對瀏覽器環境):JS自帶的任務,包括promise的then和catch,MutationObserver;

  任務佇列:由巨集任務佇列與微任務佇列組成;

  event loop:又名事件迴圈,它會不斷地去微任務佇列裡取任務放到執行棧執行,當微任務佇列被清空時,才去巨集任務佇列取任務執行。假如這個巨集任務裡面碰到了微任務和巨集任務,會分別推入到任務佇列中,並按照“只有微任務佇列清空才執行巨集任務”的原則來執行程式碼;

  

畫圖理解

  

 

過程敘述  

  當我們說“瀏覽器是 JS 的家”時,這句話真正的意思是瀏覽器提供執行時環境來執行我們的JS程式碼。

  瀏覽器的主要元件包括JS引擎,事件迴圈,任務佇列和Web APIs。上圖的任務佇列有一點瑕疵,任務佇列應該有兩個(巨集任務佇列與微任務佇列)。

  JS引擎從堆中取出JS程式碼並及進行分析。假設是一段或多段script程式碼,它會被認為是一個或幾個巨集任務,並推入到web apis中。web apis看到是script程式碼,就把它推入到巨集任務佇列中,event loop從巨集任務佇列取到這一段JS程式碼,並放入執行棧中執行。

  在執行當前巨集任務時,每當它遇到一些非同步程式碼,如setTimeout

,它又會把它推入到web apis中去執行。當非同步程式碼在Web APIs被執行完後,其回撥callback 就被送往任務佇列。

  event loop不斷地監視任務佇列(Task Queue),並按它們排隊的順序一次處理一個回撥。每當呼叫棧為空,也就是同步程式碼執行完畢時,event loop會不斷地去微任務佇列裡取任務放到執行棧執行,當微任務佇列被清空時,才去巨集任務佇列取任務執行。請記住,如果呼叫棧不是空的,則事件迴圈不會將任何回撥推入執行棧。

 

例題分析

  

 

   按照我們之前的理論,首先這裡分成兩個巨集任務,script1和script2。

  script1的同步程式碼先執行,列印111,333。微任務佇列推入222,巨集任務佇列推入444。

  此時微任務佇列只有222,而巨集任務佇列為“script2 --- 444”。

  所以先清空微任務佇列,打印出222,再從巨集任務佇列中取出script2,推到執行棧中執行。

  script2的同步程式碼先執行,列印555,777。並將666推入微任務佇列,888推入巨集任務佇列。

  此時微任務佇列只有666,而巨集任務佇列為“444 --- 888”。

  所以先清空微任務佇列,打印出666,再從巨集任務佇列依次取出444和888,並推到執行棧中執行。

  綜上,列印結果如下:

  

 

 

  

&n