最近一直在研究event loop相關的,首先我們可以從HTML standard,標準中對於event loop的介紹。
To coordinate events, user interaction, scripts, rendering, networking, and so forth, user agents must use event loops as described in this section. There are two kinds of event loops: those for browsing contexts, and those for workers.
為了協調事件,使用者互動,指令碼,渲染,網路請求,等等,必須用到event loop。而event loop有兩種型別,一種browsing contexts和另一種workers。
task
一個event loop中會有一個或者多個task佇列。而來源不同的task將會放入到不同的task中。
典型的任務源:
- DOM操作
- 使用者互動(點選事件之類的)
- 網路請求
- script程式碼
- setTimeout/setInterval
- I/O
- UI互動
- setImmediate(nodejs環境中)
大致有這麼幾種,關於macrotask這個說法,並沒有在標準中被提及。
Microtask
一個事件迴圈只有一個Microtask,以下幾種任務被認為是microtask
- promise
- promise回撥(then和catch)
- MutationObserver
- process.nextTick(nodejs環境中)
關於EL的機制
可以觀看標準的8.1.4.2 processing model中的解釋,過程很長,很複雜。
但是其實總結的來說就是以下這段話:
先執行一個task,然後再去清空Microtask佇列,在執行一個task,然後再去清空Microtask佇列
那麼我們來看一個例子:
setTimeout(()=>{
console.log('timer1')
Promise.resolve().then(function() {
console.log('promise1')
})
}, 0)
setTimeout(()=>{
console.log('timer2')
Promise.resolve().then(function() {
console.log('promise2')
})
setTimeout(() => {
console.log('timer3')
}, 0)
}, 0)
Promise.resolve().then(function() {
console.log('promise3')
})
console.log('start')
複製程式碼
當然,答案不重要,其中的過程能理解最重要。
- 迴圈1
執行過程: script指令碼被當作一個task,放入到task佇列中
【task佇列】: 1、將定時器set1和set2放入到task佇列中。 2、將Promise放入到microtask佇列中 3、輸出start
【microtask佇列】: 1、執行promise的回撥,輸出promise3
- 迴圈2
執行過程: 在task佇列中,將set1當作一個task。
【task佇列】: 1、輸出timer1 2、將promise放入到microtask佇列中
【microtask佇列】 1、執行promise的回撥,輸出promise1
- 迴圈3
執行過程: 在task佇列中,將set2當作一個task。
【task佇列】: 1、輸出timer2 2、將promise放入到microtask中 3、將set3放入到task佇列中
【microtask】 1、執行promise的回撥,輸出promise2
- 迴圈4
執行過程: 在task佇列中,將set3當作一個task。
【task佇列】: 1、輸出timer3 2、經過 microtask checkpoint檢測,microtask佇列為空,跳過。
- 答案:
start
promise3
timer1
promise1
timer2
promise2
timer3
複製程式碼