js的事件循環機制和任務隊列


 

上篇講異步的時候,提到了同步隊列和異步隊列的說法,其實只是一種形象的稱呼,分別代表主線程中的任務和任務隊列中的任務,那么此篇我們就來詳細探討這兩者。

一、來張圖感受一下

如果看完覺得一臉懵逼,請繼續往下看。

二、解析

我們還是拿上篇的例子做解析

step1:f1、Promise對象實例化、f2被放入主線程的堆內存中;

step2:Promise對象實例化后的同步代碼塊被放入主線程的執行棧中執行,並且產生的異步任務被放入任務隊列;

step3:f1被放入主線程的執行棧中執行(打印“我是F1”),for循環產生的1000個定時器(異步任務)放入任務隊列;

step4:f2被放入主線程的執行棧中執行,連續5次;

step5:至此,主線程的執行棧中已經沒有任務了,於是事件循環(event loop)機制從任務隊列中取出一個任務放入主線程的執行棧中執行;

step6:等待主線程的執行棧中又沒有任務了,事件循環機制再次去任務隊列中取出任務;

step7:重復第6步。

 三、任務隊列

上面提到了任務隊列,任務隊列就是等候執行的一系列任務,就好比鍋里的飯,你只有把碗里的飯吃完了,才能再次去鍋里再盛一碗(不要杠!);

只有主線程的執行棧中沒有了任務,事件循環機制才會去任務隊列拿任務去執行。

由剛開始的圖,你也看到了,任務隊列是分不同類別並且是有優先級的。

  • 任務隊列又分為macro-task(宏任務)和micro-task(微任務);
  • macro-task大概包括:script(整體代碼),setTimeout,setInterval,setImmediate,I/O,UI rendering;
  • micro-task大概包括:process.nextTick,Promise,Object.observe(已廢棄),MutationObserver(html5新特性)
  • setTimeout/Promise等我們稱之為任務源。而進入任務隊列的是他們指定的具體執行任務。
  • 來自不同任務源的任務會進入到不同的任務隊列

優先級的話,micro-task > macro-task;

對於micro-task:process.nextTick > Promise.then

對於macro-task:setTimeout > setImmediate

具體的案例演示,請參照這篇文章: 事件循環的順序(優先級)
四、簡單demo帶你理解事件循環
Promise.resolve().then(()=>{
  console.log('Promise1')  
  setTimeout(()=>{
    console.log('setTimeout2')
  },0)
})

setTimeout(()=>{
  console.log('setTimeout1')
  Promise.resolve().then(()=>{
    console.log('Promise2')    
  })
},0)

step1:

分析:開始任務隊列里有微任務promise1和宏任務timeout1,第一次事件循環一看有微任務,二話不說,直接拿promise1到主線程跑(其實是跑完所有的微任務);promise1運行結果是,控制台打印promise1,並生成一個宏任務timeout2。

step2:

分析:因為此時任務隊列里只有宏任務,於是,根據隊列規則以及優先級(這里只有一種宏任務,所以沒有涉及到優先級),事件循環拿timeout1去主線程跑;

timeout1運行結果,打印setTimeout1,並生成一個微任務promise2,至此第一次事件循環結束。

 

step3:

分析:任務隊列里有微任務promise2和宏任務timeout2,事件循環一看有微任務,二話不說,直接拿promise2到主線程跑;

運行結果,控制台打印promise2,此時任務隊列只剩下一個宏任務timeout2。

step4:

分析:此時任務隊列只有一個宏任務timeout2,事件循環二話不說,因為沒得挑了嘛,直接拿到主線程去跑,控制台打印timeout2,至此結束,也是第二次事件循環結束。

所以,一次事件循環是跑完所有微任務並推一個宏任務到主線程的過程。

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM