js 事件循環消息隊列和微任務宏任務


事件循環與消息隊列

因為js是單線程腳本語言,一般情況下代碼是同步執行。也就是說js執行代碼是一行一行向下執行的,前面沒有執行完成是不會執行后面的代碼的。

  • 同步和異步的區別其實就在於需不需要排隊的問題
    • 同步:所有任務一視同仁,都得排隊,先來后到;
    • 異步:可以按照一定規則(不至於亂套)插隊執行;
  • 事件循環和消息隊列怎么理解
    • 事件循環:單線程腳本語言javascript處理任務的一種執行機制,通過循環來執行任務隊列里的任務。這個執行過程形象的稱之為事件循環
    • 消息隊列:js為單線程腳本語言,執行任務時需要排隊,每當有新的任務來臨時就加到這個隊列后面。這個隊列就叫消息隊列或者任務隊列

瀏覽器與Node的事件循環有何區別?

  • 瀏覽器事件循環過程
  當某個宏任務執行完后,會查看是否有微任務隊列。
  如果有,先執行微任務隊列中的所有任務,
  如果沒有,會讀取宏任務隊列中排在最前的任務,執行宏任務的過程中,遇到微任務,依次加入微任務隊列。
  棧空后,再次讀取微任務隊列里的任務,依次類推。
  • node事件循環過程
1.外部輸入數據
2.輪詢階段(poll)
3.檢查階段(check)
3.關閉事件回調階段(close callback)
4.定時器檢測階段(timer)
5.I/O事件回調階段(I/O callbacks)
6.閑置階段(idle, prepare)
7.輪詢階段(按照該順序反復運行)...

timers 階段:這個階段執行timer(setTimeout、setInterval)的回調
I/O callbacks 階段:處理一些上一輪循環中的少數未執行的 I/O 回調
idle, prepare 階段:僅node內部使用
poll 階段:獲取新的I/O事件, 適當的條件下node將阻塞在這里
check 階段:執行 setImmediate() 的回調
close callbacks 階段:執行 socket 的 close 事件回調

注意:
上面六個階段都不包括 process.nextTick(),這個函數其實是獨立於事件循環之外的,它有一個自己的隊列,當每個階段完成后,
如果存在 nextTick 隊列,就會清空隊列中的所有回調函數,並且優先於其他微任務執行

微任務和宏任務

在js中,任務可以分為同步任務和異步任務,也可以分為微任務和宏任務。同步任務屬於宏任務,有了這些划分,就可以保證所有任務都有條不紊的執行下去,總的來說就是給要執行的任務定了執行規則、划分了優先級。
在總結宏任務與微任務時,我們先要知道我們哪些情況下可能會執行異步操作(未來某個時間執行任務);然后要知道宏任務與微任務是怎么區分的,哪些屬於宏任務,哪些屬於微任務;最后我們要知道宏任務與微任務是通過什么規則來配合執行的。

  • 可能存在異步執行的情況

    1. 回調函數 callback
    2. Promise/async await
    3. Generator 函數
    4. 事件監聽
    5. 發布/訂閱
    6. 計時器
    7. requestAnimationFrame
    8. MutationObserver
    9. process.nextTick
    10. I/O
  • 宏任務:

  • 微任務:

  • 注:

    • process.nextTick(它指定的任務總是發生在所有異步任務之前),網上幾乎無一例外說這是微任務,可是只要存在這個,process.nextTick就會在所有異步任務執行之前執行
    • 事件監聽, 比如addeventlistener。宏任務待驗證
    • 發布/訂閱 宏任務待驗證
    • 有人說同步任務屬於宏任務,關於這中說法我覺得不太准確,應該說同步任務的執行優先級是高於異步任務
  • 任務執行過程

    1. 所有任務都在主進程上執行,異步任務會經歷2個階段 Event Table和Event Queue
    2. 同步任務在主進程排隊執行,異步任務(包括宏任務和微任務)在事件隊列排隊等待進入主進程執行
    3. 遇到宏任務推進宏任務隊列,遇到微任務推進微任務隊列(宏任務隊列的項一般對應一個微任務隊列,有點像一個大哥帶着一群小馬仔,這就組成一組異步任務。如果有嵌套那就會有多個大哥小馬仔)
    4. 執行宏任務,執行完宏任務,檢查有沒有當前層的微任務(大哥帶着小馬仔逐步亮相。。。)
    5. 繼續執行下一個宏任務,然后執行對應層次的微任務,直到全部執行完畢(下一個大哥帶着他的小馬仔亮相。。。)
  • 盜圖兩張

    • 同步任務與異步任務執行流程
      同步任務與異步任務執行流程
    • 微任務與宏任務執行流程
      微任務與宏任務執行流程

舉個栗子

下面舉個例子加深印象,例子來自網絡。當存在多個不同的異步操作時,看宿主環境(node、瀏覽器等等)是怎么執行的,可以把下面代碼或者練習題的代碼拷出來,利用瀏覽器斷點看下執行過程。

//因為涉及到process 所以應該在node環境下執行  


console.log('1') //主進程 執行 
setTimeout(function() {
   console.log('2')   //因為setTimeout是宏任務,所以加入宏任務隊列1,['2']
   process.nextTick(function() {
     console.log('3') //因為 process.nextTick是微任務,所以加入微任務隊列2,['4','3']
  })
  new Promise(function(resolve) {
      console.log('4') //因為此處代碼執行不屬於異步,所以直接推入主程序執行,['4']
      resolve()
  }).then(function() {
    console.log('5') // 因為promise then 是微任務,所以推入微任務隊列2,['4','3','5']
  })
},0)
// process.nextTick總是發生在所有異步任務之前
process.nextTick(function() {
  console.log('6')  //因為process.nextTick是微任務,所以推入微任務隊列1,['6']
  new Promise(function(resolve) {
    console.log('7')//因為此處代碼執行不屬於異步,所以直接推入主程序執行,['6','7']
    resolve()
  }).then(function() {
    console.log('8')//因為 promise then 是微任務,所以推入微任務隊列1,['6','7','8']
  })
  setTimeout(function() {
    console.log('9')//因為setTimeout是宏任務,所以推入宏任務隊列2 ,['9']
    process.nextTick(function() {
      console.log('10')//因為process.nextTick是微任務,所以推入微任務隊列3,['9','11','12','10']
    })
    new Promise(function(resolve) {
      console.log('11')//因為此處代碼執行不屬於異步,所以直接推入主程序執行,['9','11']
      resolve()
      console.log('12')////因為此處代碼執行不屬於異步,所以直接推入主程序執行,['9','11','12']
    }).then(function() {
      console.log('13')//因為 promise then 是微任務,所以推入微任務隊列3,['9','11','12','10','12']
    })
  },0)
})

//打印輸出
// 1
// 6
// 7
// 8
// 2
// 4
// 3
// 5
// 9
// 11
// 12
// 10
// 13

得出結論:微任務優先級大小:process.nextTick > setTimeout

練習題

字節筆試題

async function async1() {        
  console.log('async1 start');
  await async2();
  console.log('async1 end');
}
async function async2() {
  console.log('async2'); 
}

console.log('script start'); 
setTimeout(function() {
    console.log('setTimeout');
}, 0);  
async1();
new Promise(function(resolve) {
    console.log('promise1');
    resolve();
  }).then(function() {
    console.log('promise2');
});
console.log('script end');

參考


免責聲明!

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



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