作者:孫輝,美團金融前端團隊成員。15年畢業加入美團,相信技術,更相信技術只是大千世界裡知識的一種,個人部落格: sunyuhui.com
前言
JavaScript中的事件迴圈一直都是一個很多人都或多或少了解,但說不清楚的知識點,停留在一知半解的層面。以前只需要使用回撥函式、定時器還好說,但是自從有了Promise
之後,對事件迴圈的透徹瞭解就比較重要了。
本篇文章不打算從頭開始敘述,那樣篇幅太長,略過最基本的概念,我們簡單粗暴的把事件迴圈說清楚。
理論:關於MacroTask和MicroTask
一張圖展示JavaScript中的事件迴圈:
一次事件迴圈:先執行macroTask
佇列中的一個,然後執行microTask
佇列中的所有任務。接著開始下一次迴圈(只是針對macroTask和microTask,一次完整的事件迴圈會比這個複雜的多)。
其中macroTask
和microTask
是兩種任務佇列,相比而言,大家更熟悉的一個詞是任務佇列(task queue
,其實就是macroTask
),大家更熟悉的關於事件迴圈的機制說法大概是:主程式執行完了之後,每次從任務佇列裡取一個任務執行。但是promise
出現之後,這個說法就不太準確了。
JavaScript引擎對這兩種佇列有不同的處理,簡單的說就是引擎會把我們的所有任務分門別類,一部分歸為macroTask
,另外一部分歸為microTask
,下面是類別劃分:
- macroTask: setTimeout, setInterval, setImmediate, requestAnimationFrame, I/O, UI rendering
- microTask: process.nextTick, Promise, Object.observe, MutationObserver
我們所熟悉的定時器就屬於macroTask
,但是僅僅瞭解macroTask
的機制還是不夠的。
上面這些都是理論啊,我們怎麼直觀的感受兩種佇列的區別呢?說的再多也不如來一段實踐,我們感知這種區別最好的方式就是通過任務的執行順序。
實踐:小二,上程式碼
我們以setTimeout
、process.nextTick
、promise
為例直觀感受下兩種任務佇列的執行方式。
console.log(`main1`);
process.nextTick(function() {
console.log(`process.nextTick1`);
});
setTimeout(function() {
console.log(`setTimeout`);
process.nextTick(function() {
console.log(`process.nextTick2`);
});
}, 0);
new Promise(function(resolve, reject) {
console.log(`promise`);
resolve();
}).then(function() {
console.log(`promise then`);
});
console.log(`main2`);複製程式碼
彆著急看答案,先以上面的理論自己想想,執行結果會是啥?
來個圖片佔個位
最終結果是這樣的:
main1
promise
main2
process.nextTick1
promise then
setTimeout
process.nextTick2複製程式碼
process.nextTick
和 promise then
在 setTimeout
前面輸出,已經證明了macroTask
和microTask
的執行順序。但是有一點必須要指出的是。上面的圖容易給人一個錯覺,就是主程式的程式碼執行之後,會先呼叫macroTask
,再呼叫microTask
,這樣在第一個迴圈裡一定是macroTask
在前,microTask
在後。
但是最終的實踐證明:在第一個迴圈裡,process.nextTick1
和promise then
這兩個microTask
是在setTimeout
這個macroTask
裡之前輸出的,這是為什麼呢?
因為主程式
的程式碼也屬於macroTask
(這一點我比較疑惑的是主程式都是一些同步程式碼,而macroTask和microTask包含的都是一些非同步任務,為啥主程式的程式碼會被劃分為macroTask,不過從實踐來看確實是這樣,而且也有理論支撐:【翻譯】Promises/A+規範)。
主程式這個macroTask
(也就是main1
、promise
和main2
)執行完了,自然會去執行process.nextTick1
和promise then
這兩個microTask
。這是第一個迴圈。之後的setTimeout
和process.nextTick2
屬於第二個迴圈
別看上面那段程式碼好像特別繞,把原理弄清楚了,都一樣 ~
requestAnimationFrame、Object.observe(已廢棄) 和 MutationObserver這三個任務的執行機制大家可以從上面看到,不同的只是具體用法不同。重點說下UI rendering
。在HTML規範:event-loop-processing-model裡敘述了一次事件迴圈的處理過程,在處理了macroTask
和microTask
之後,會進行一次Update the rendering,其中細節比較多,總的來說會進行一次UI的重新渲染。
後續
不知道大家有沒有發現一個現象,在學習技術點的時候,如果太淺,得來的知識點可能不完整甚至是錯的,如果追究的太深,又會給人一種太偏學究的感覺,其中的平衡點,大家自己留心把握。
done
另外,大家端午快樂 ~
最後,團隊為了招聘方便,整了個公眾號,主要是一些招聘資訊,團隊資訊,所有的技術文章在公眾號裡也可以看到,對了,如果你想去美團其他團隊,我們也可以幫你內推哦 ~
參考: