ES6系列--【事件迴圈 EventLoop(Promise,setTimeOut,async/await執行順序)】
事件迴圈執行機制
(1)執行一個巨集任務(棧中沒有就從事件佇列中獲取)
(2)執行過程中如果遇到微任務,就將它新增到微任務的任務佇列中;
(3)巨集任務執行完畢後,立即執行當前微任務佇列的所有微任務;
(4)當前微任務執行完畢,開始檢查渲染,然後GUI執行緒接管渲染;
(5)渲染完畢後,JS執行緒繼續接管,開始下一個巨集任務。
事例:
async function async1() { console.log("async1 start"); //(2) await async2(); console.log("async1 end"); //(6) } async function async2() { console.log( 'async2'); //(3) } console.log("script start"); //(1) setTimeout(function () { console.log("settimeout"); //(8) },0); async1(); new Promise(function (resolve) { console.log("promise1"); //(4) resolve(); }).then(function () { console.log("promise2"); //(7) }); console.log('script end');//(5)
按照事件迴圈機制分析以上程式碼執行流程:
1. 首先,事件迴圈從巨集任務(macrotask)佇列開始,首先讀取script(整體程式碼)任務;當遇到任務源(task source)時,則會先分發任務到對應的任務佇列中去。
2. 然後我們看到首先定義了兩個async函式,此時沒有呼叫,接著往下看,然後遇到了 `console` 語句,直接輸出 `script start`。輸出之後,script 任務繼續往下執行,遇到 `setTimeout`,其作為一個巨集任務源,則會先將其任務分發到對應的任務佇列中。
3. script 任務繼續往下執行,執行了async1()函式,async函式中在await之前的程式碼是立即執行的,所以會立即輸出`async1 start`。
遇到了await時,會將await後面的表示式執行一遍,所以就緊接著輸出`async2`,然後將await後面的程式碼也就是`console.log('async1 end')`加入到microtask中的Promise佇列中,接著跳出async1函式來執行後面的程式碼。
4. script任務繼續往下執行,遇到Promise例項。由於Promise中的函式是立即執行的,而後續的 `.then` 則會被分發到 microtask 的 `Promise` 佇列中去。所以會先輸出 `promise1`,然後執行 `resolve`,將 `promise2` 分配到對應佇列。
5. script任務繼續往下執行,輸出了 `script end`,至此,全域性任務就執行完畢了。
根據上述,每次執行完一個巨集任務之後,會去檢查是否存在 Microtasks;如果有,則執行 Microtasks 直至清空 Microtask Queue。
因而在script任務執行完畢之後,開始查詢清空微任務佇列。此時,微任務中, `Promise` 佇列有的兩個任務`async1 end`和`promise2`,因此按事件佇列先進先出的原則,先後順序輸出 `async1 end,promise2`。當所有的 Microtasks 執行完畢之後,表示第一輪的迴圈就結束了。
6.第二輪迴圈依舊從巨集任務佇列開始。此時巨集任務中只有一個 `setTimeout`,取出直接輸出即可,至此整個流程結束。