前言
本來想復習一下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最大的區別在它們會被放置在不同的任務調度隊列中。我在網上盜了一張圖,如下所示:
每一次事件循環中,主進程都會先執行一個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
大致流程如下所示:
先運行主程序(事實上主程序本身就是一個macroTask),主程序把
setTimeout
和process.nextTick
分別放入MacroTask Queue和MicroTask Queue主程序結束,這時候我們看到了第一二條的打印結果main start、main end
如上面所提到的,每一個macroTask結束后會開始消費microTask。這時的MicroTask Queue里有一個
process.nextTick
,然后發現它本身也調用了一個process.nextTick
,所以繼續把這個內層的任務加入MicroTask Queue。線程消費掉所有MicroTask Queue里的任務(這時只有兩個任務),我們得到了第三四條結果process.nextTick 1和process.nextTick 2
當MicroTask Queue清空后,Event Loop進入下一個循環:執行MacroTask Queue的
setTimeout
任務,然后得到了第五條輸出setTimeout,之后它還會把又一個process.nextTick
放入MicroTask Queue繼續如4所示過程,Event Loop在Current MacroTask執行完成后消費MicroTask Queue,這時候我們有了最后一條輸出process.nextTick 3
上面的代碼看著可能有點繞,但是弄清楚原理后還是能理解的。
當然現實中,Event Loop的任務調度會很復雜,比如:
主程序里同時調用多個setTimeout,誰先執行是不確定的。
Node.js文檔里稱“setImmediate指定的回調函數,總是排在setTimeout前面”,所以應該是有多個Task Queue的
瀏覽器的話,處理了macroTask和microTask之后,會進行一次Update the rendering,具體細節還很多
后話
學習語言的底層實現有時候是一件很無聊的事,類似于“孔乙己中,茴香豆的‘茴’字有幾種寫法”。說實在,現實開發中很少人能用到。不過,假如你并不滿足于CURD工程師的話,最好還是能了解一下這類知識。至少在某些場合里也有了一定的“談資”。互勉!