JS中的宏任務和微任務的區別和用法

宏任務(macrotask )和微任務(microtask )

宏任務和微任務都是我們在開發工作中經常用到的。

宏任務(macrotask )和微任務(microtask )

macrotask 和 microtask 表示異步任務的兩種分類。

在掛起任務時,JS 引擎會將所有任務按照類別分到這兩個隊列中,首先在 macrotask 的隊列(這個隊列也被叫做 task queue)中取出第一個任務,執行完畢后取出 microtask 隊列中的所有任務順序執行;之后再取 macrotask 任務,周而復始,直至兩個隊列的任務都取完。

“先查看是否有事件可執行”,如果有這優先級限制,那應該可以這樣理解:

其實并不止一個消息隊列,有異步隊列事件隊列,而事件隊列總是優先于異步隊列被空閑下來的JS線程取用

宏任務一般是:包括整體代碼scriptsetTimeoutsetIntervalI/OUI render
微任務主要是:PromiseObject.observeMutationObserver

  • 宏任務和微任務之間的關系


    宏任務和微任務之間的關系

區別體現demo

  • 1.Promise在前,setTimeout在后
new Promise((resolve) => {
    console.log('外層宏事件2');
    resolve()
}).then(() => {
    console.log('微事件1');
}).then(()=>{
    console.log('微事件2')
})
console.log('外層宏事件1');
setTimeout(() => {
    //執行后 回調一個宏事件
    console.log('內層宏事件3')
}, 0)

結果:

外層宏事件2
外層宏事件1
微事件1
微事件2
內層宏事件3
  • 2.setTimeout在前,Promise在后
setTimeout(() => {
    //執行后 回調一個宏事件
    console.log('內層宏事件3')
}, 0)
console.log('外層宏事件1');

new Promise((resolve) => {
    console.log('外層宏事件2');
    resolve()
}).then(() => {
    console.log('微事件1');
}).then(()=>{
    console.log('微事件2')
})

結果:

外層宏事件1
外層宏事件2
微事件1
微事件2
內層宏事件3

NodeJS的宏任務和微任務跟瀏覽器環境又有什么區別

瀏覽器的Event loop是在HTML5中定義的規范,而node中則由libuv庫實現。libuv庫流程大體分為6個階段:timers,I/O callbacks,idle、prepare,poll,check,close callbacks,和瀏覽器的microtask,macrotask那一套有區別。
兩者執行的規則不同,所以不相同。

//            js event loop (指主線程從“任務隊列”中循環讀取任務)
//
//             event queue 可能同時不止一個
//
//            宏任務(macrotask)event queue (macro-task(宏任務):script(主程序代碼),,setTimeout,setInterval, I/O, setImmediate(node環境),UI rendering)
//            ^
//            | 異步
//     主線程->
//          | |  異步
//          | V
//          | 微任務(microtask) event queue (micro-task(微任務):Promise,process.nextTick(node環境),Object.observe, MutationObserver)
//          |
//          V
//          如果是瀏覽器環境 click等事件隊列總是優先于異步隊列被空閑下來的JS線程取用

// 執行順序   script(主程序代碼)—>process.nextTick—>Promises…——>setTimeout——>setInterval——>setImmediate——> I/O——>UI rendering

// nextTick 優先級比 Promise 等 microTask 高,setTimeout和setInterval優先級比setImmediate高

瀏覽器環境

瀏覽器環境下的 異步任務 分為 宏任務(macroTask) 和 微任務(microTask):

宏任務(macroTask):script 中代碼、setTimeoutsetIntervalI/OUI render

微任務(microTask): PromiseObject.observeMutationObserver

當滿足執行條件時,宏任務(macroTask) 和 微任務(microTask) 會各自被放入對應的隊列:宏隊列(Macrotask Queue) 和 微隊列(Microtask Queue) 中等待執行。

Node 環境

在 Node 環境中 任務類型 相對就比瀏覽器環境下要復雜一些:

microTask:微任務;

nextTick:process.nextTick;

timers:執行滿足條件的 setTimeout 、setInterval 回調;

I/O callbacks:是否有已完成的 I/O 操作的回調函數,來自上一輪的 poll 殘留;

poll:等待還沒完成的 I/O 事件,會因 timers 和超時時間等結束等待;

check:執行 setImmediate 的回調;

close callbacks:關閉所有的 closing handles ,一些 onclose 事件;
idle/prepare 等等:可忽略。

因此,也就產生了執行事件循環相應的任務隊列 Timers Queue、I/O Queue、Check Queue 和 Close Queue。
2.執行過程
瀏覽器環境
先執行<script>中的同步任務,然后所有微任務,一個宏任務,所有微任務,一個宏任務......

執行完主執行線程中的任務;

取出 Microtask Queue 中任務執行直到清空;

取出 Macrotask Queue 中一個任務執行;

重復 2 和 3 。

需要 注意 的是:

在瀏覽器頁面中可以認為初始執行線程中沒有代碼,每一個<script>中的代碼是一個獨立的 task ,即會執行完前面的<script>中創建的 microTask 再執行后面的<script>中的同步代碼;
如果 microTask 一直被添加,則會繼續執行 microTask ,“卡死” macroTask;
部分版本瀏覽器有執行順序與上述不符的情況,可能是不符合標準或 js 與 html 部分標準沖突;
Promise 的thencatch才是 microTask ,本身的內部代碼不是;
個別瀏覽器獨有API未列出。

Node 環境
循環之前
在進入第一次循環之前,會先進行如下操作:

同步任務;
發出異步請求;
規劃定時器生效的時間;
執行process.nextTick()

開始循環
循環中進行的操作:

清空當前循環內的 Timers Queue,清空 NextTick Queue,清空 Microtask Queue
清空當前循環內的 I/O Queue,清空NextTick Queue,清空 Microtask Queue
清空當前循環內的 Check Queue,清空 NextTick Queue,清空 Microtask Queue
清空當前循環內的 Close Queue,清空 NextTick Queue,清空Microtask Queue
進入下輪循環。

可以看出,nextTick 優先級比 PromisemicroTask 高,setTimeoutsetInterval優先級比setImmediate高。
注意
在整個過程中,需要 注意 的是:

如果在 timers 階段執行時創建了setImmediate 則會在此輪循環的 check 階段執行,如果在 timers 階段創建了setTimeout,由于 timers 已取出完畢,則會進入下輪循環,check 階段創建 timers 任務同理;

setTimeout優先級比setImmediate高,但是由于setTimeout(fn,0)的真正延遲不可能完全為 0 秒,可能出現先創建的setTimeout(fn,0)而比setImmediate的回調后執行的情況。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容