瞭解js執行機制——微任務與巨集任務

石燕平發表於2019-06-04

由一道面試題引發的思考。

setTimeout(function() {
  console.log(4);
}, 0);

new Promise(function(reslove) {
  console.log(1);
  reslove();
}).then(function(data) {
  console.log(3);
});

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

會輸出:1,2,3,4。我們來想一下為什麼。

瀏覽器中的事件迴圈 eventLoop,分為同步執行棧和非同步佇列,首先會執行同步的任務,當同步任務執行完之後會從非同步佇列中取非同步任務拿到同步執行棧中進行執行。

在取非同步佇列時,還會有一個區分,就是區分微任務和巨集任務。

  • microtask:微任務,優先順序高,並且可以插隊,不是先定義先執行。包括:promise 中的 then,observer,MutationObserver,setImmediate
  • macrotask:巨集任務,優先順序低,先定義的先執行。包括:ajax,setTimeout,setInterval,事件繫結,postMessage,MessageChannel(用於訊息通訊)

因為微任務的優先順序較高,所以會先將微任務的非同步任務取出來進行執行,當微任務的任務都執行完畢之後,會將巨集任務中的任務取出來執行。

我們這次來看一下上面的題,promise 中是同步任務,promise 的 .then 中是非同步任務,並且是微任務。使用 setTimeout 是巨集任務,即使是延時為 0,也是巨集任務。

所以上面的執行順序就是先將 setTimeout 加入到非同步佇列的巨集任務池中,然後執行 promise 中的console.log(1),再將 promise 的.then 加到非同步佇列中微任務池中,再執行同步任務console.log(2),當同步任務都執行完之後,去微任務中的任務,執行console.log(3),微任務執行完之後取巨集任務,執行console.log(4)。所以順序就是:1,2,3,4。

擴充套件:

將這道面試題進行一些改造:

setTimeout(function() {
  console.log(4);
}, 0);

new Promise(function(reslove) {
  console.log(1);
  setTimeout(function() {
    reslove('done');
  }, 0);
  reslove('first');
}).then(function(data) {
  console.log(data);
});

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

這個時候就會輸出:1,2,first,4,沒有輸出 done,有些人可能會想,應該輸出 1,2,first,4,done,這個時候你就要知道,當使用 reslove 之後,promise 的狀態就從 pedding 變成了 resolve,promise 的狀態更改之後就不能再更改了,所以 reslove('done') 就不會執行了。

當我們把 reslove('done') 改成 console.log('done') 的時候,他就會輸出 1,2,first,4,done 了。

再做一個更復雜的變更:

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

new Promise(function(reslove) {
  console.log(2);
  reslove('p1');
  new Promise(function(reslove) {
    console.log(3);
    setTimeout(function() {
      reslove('setTimeout2');
      console.log(4);
    }, 0);
    reslove('p2');
  }).then(function(data) {
    console.log(data);
  });
  setTimeout(function() {
    reslove('setTimeout1');
    console.log(5);
  }, 0);
}).then(function(data) {
  console.log(data);
});

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

輸出的結果是:2,3,6,p2,p1,1,4,5。

先執行同步的任務,new Promise 中的都是同步任務,所以先輸出 2,3,6,然後再執行微任務的,微任務可以插隊,所以並不是先定義的 p1 先執行,而且先將 p2 執行,然後執行 p1,當微任務都執行完成之後,執行巨集任務,巨集任務依次輸出 1,4,5,promise 的狀態不可以變更,所以 setTimeout1 和 setTimeout2 不會輸出。

閱讀完後兩部曲

  1. 喜歡的小夥伴點個贊吧,感覺對身邊人有幫助的,麻煩動動手指,分享一下。非常感謝各位花時間閱讀完,同時很感謝各位的點贊和分享。
  2. 希望各位關注一下我的公眾號吧,新的文章第一時間發到公眾號,公眾號主要發一些個人隨筆、讀書筆記、還有一些技術熱點和實時熱點,並且還有非常吸引人的我個人自費抽獎活動哦~

瞭解js執行機制——微任務與巨集任務

相關文章