MacroTask & MicroTask

前言

本來想復習一下javascript事件循環(Event Loop)的,但是發現太巨大了。最近工作也有些忙,所以還是刪繁就簡,集中精力寫某一個知識點比較現實。這次講講Event Loop里的MacroTask(宏任務)和MicroTask(微任務)。

Task Queue

MacroTask和MicroTask主要有如下若干種方法:

  • macrotasks: setTimeout, setInterval, setImmediate, requestAnimationFrame, I/O, UI rendering

  • microtasks: process.nextTick, Promises, Object.observe, MutationObserver

JS開發人員應該對這些方法都不會太陌生——都是些常見的異步操作。但這些方法在執行時有什么區別呢?通俗來說,macrotasks和microtasks最大的區別在它們會被放置在不同的任務調度隊列中。我在網上盜了一張圖,如下所示:

Task Queue

每一次事件循環中,主進程都會先執行一個macroTask任務,這個任務就來自于所謂的MacroTask Queue隊列;當該macroTask執行完后,Event loop會立馬調用microTask隊列的任務,直到消費完所有的microtask,再繼續下一個事件循環。

管中窺豹,microTask調用優先級較高于macroTask.

DEMO

OK,僅僅看一張圖似乎過于抽象,還是用code來解釋一下:

console.log('main start');

setTimeout(() => {
    console.log('setTimeout');
    process.nextTick(() => console.log('process.nextTick 3'));
}, 0);

process.nextTick(() => {
    console.log('process.nextTick 1');
    process.nextTick(() => console.log('process.nextTick 2'));
});

console.log('main end');

我們上面提到過process.nextTick是microTask,setTimeout是macroTask,我這里嵌套了宏任務和微任務,看看它們的執行順序是怎么樣的:

1   main start
2   main end
3   process.nextTick 1
4   process.nextTick 2
5   setTimeout
6   process.nextTick 3

大致流程如下所示:

  1. 先運行主程序(事實上主程序本身就是一個macroTask),主程序把setTimeoutprocess.nextTick分別放入MacroTask Queue和MicroTask Queue

  2. 主程序結束,這時候我們看到了第一二條的打印結果main startmain end

  3. 如上面所提到的,每一個macroTask結束后會開始消費microTask。這時的MicroTask Queue里有一個process.nextTick,然后發現它本身也調用了一個process.nextTick,所以繼續把這個內層的任務加入MicroTask Queue。

  4. 線程消費掉所有MicroTask Queue里的任務(這時只有兩個任務),我們得到了第三四條結果process.nextTick 1process.nextTick 2

  5. 當MicroTask Queue清空后,Event Loop進入下一個循環:執行MacroTask Queue的setTimeout任務,然后得到了第五條輸出setTimeout,之后它還會把又一個process.nextTick放入MicroTask Queue

  6. 繼續如4所示過程,Event Loop在Current MacroTask執行完成后消費MicroTask Queue,這時候我們有了最后一條輸出process.nextTick 3

Task Invocation

上面的代碼看著可能有點繞,但是弄清楚原理后還是能理解的。
當然現實中,Event Loop的任務調度會很復雜,比如:

  • 主程序里同時調用多個setTimeout,誰先執行是不確定的。

  • Node.js文檔里稱“setImmediate指定的回調函數,總是排在setTimeout前面”,所以應該是有多個Task Queue的

  • 瀏覽器的話,處理了macroTask和microTask之后,會進行一次Update the rendering,具體細節還很多

后話

學習語言的底層實現有時候是一件很無聊的事,類似于“孔乙己中,茴香豆的‘茴’字有幾種寫法”。說實在,現實開發中很少人能用到。不過,假如你并不滿足于CURD工程師的話,最好還是能了解一下這類知識。至少在某些場合里也有了一定的“談資”。互勉!

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

推薦閱讀更多精彩內容