在前端面試中,面試官可能會問你V8的引擎機制,給你這樣的一道題目如下:
console.log(1);
setTimeout(() => {
console.log(2);
});
new Promise(reslove => {
console.log(3);
reslove();
}).then(() => {
console.log(4);
});
console.log(5);
面試官會讓你給出題目中輸出的順序以及執行原理
直接在瀏覽器運行我們可以知道輸出的順序是1、3、5、4、2,那這過程中到底為什麼會這樣輸出呢?要了解這個打印順序的原理,我們必須弄清楚V8的事件輪詢機制。下面是一張原理圖:
裏面涉及到兩個概念,一個是宏任務MacroTask,一個是微任務MicroTask。其中MacroTask所存放的任務隊列有setTimeout,setinterval。MicroTask裏面所存放的隊列有Promise,.then,還有process,.nextTick等等。
它輪詢具體的方式是,我們這裏假定一個流程,如下圖:
從開始執行,代碼優先執行同步的script片段,然後我們就一直把同步的執行完。執行完同步之後的我們一旦遇到有異步的代碼,我們就開始進入任務類型的判斷,如果判斷應該存放於MacroTask的,我們就放到宏任務隊列裏,如果需要放到MicroTask,我們就存放到微任務隊列裏。
等到所有的同步代碼執行完畢之後,我們優先從微任務裏面去把任務拉取出來執行,其次才是從宏任務隊列裏面拉任務出來執行,然後如此遞歸循環,直到把所有代碼片段執行完畢。
分析:
我們再來回顧看一下上面題目裏面的代碼:
1.我們首先第一段console.log(1),那我們直接打印1。
2.然後再到setTimeout裏面有一個console.log(2),那我們就把這個console.log(2)放到MacroTask隊列。
3.然後進而我們看到new Promise,它是構造函數,那直接打印3,再到.then裏面console.log(4),我們就放到MicroTask隊列裏面,也是微任務隊列。
4.最後就是console.log(5),直接打印5。
總結:
所以經歷過第一遍分析之後,我們打印了1、3、5,等所有代碼執行完畢之後,我們優先從微任務裏面把代碼片段抽取出來,那就從console.log(4)提取出來並執行,那就打印4,最後從宏任務隊列裏面拿出console.log(2)出來執行,所以打印2。這樣下來整體的順序就會打印成1、3、5、4、2