簡述Js的事件迴圈
阿新 • • 發佈:2020-12-07
普通訊息佇列
一個程式碼塊中的所有同步程式碼,都會被看作一個巨集任務,新增到普通訊息佇列的尾部
延遲執行佇列
當你在程式碼中使用 setTimeout 或 setInterval 時,會建立一個巨集任務
延遲指定的時間後,將其放入延遲執行佇列的尾部
事件迴圈系統
為了簡化敘述,我們可以理解為:每一輪事件迴圈都執行一個巨集任務,每一輪事件迴圈都關聯一個微任務佇列
具體過程如下:
- 檢查普通訊息佇列是否為空,若不為空,則從佇列頭部取出一個巨集任務執行;否則,從延遲執行佇列頭部取出一個巨集任務執行
- 執行過程中如果遇到 setTimeout 或 setInterval,延遲指定時間後,將其作為一個巨集任務 新增到延遲執行佇列的尾部
- 執行過程中如果建立了微任務(Promise 或者 MutationObserver),就將它新增到當前的微任務佇列中
- 執行當前微任務佇列中的所有微任務
- 開始下一個迴圈
例題1:
console.log('--開始--'); setTimeout(() => { console.log('timer1'); }, 0); new Promise((resolve, reject) => { for (let i = 0; i < 5; i++) { console.log(i); } resolve() }).then(()=>{ console.log('Promise'); }) console.log('--結束--'); //---開始-- //0 //1 //2 //3 //4 //--結束-- //Promise //timer1
第一輪迴圈:普通訊息佇列中只有一個巨集任務(整個script)
- 遇到console.log,直接輸出 "--開始--"
- 遇到setTimeout,因為指定時間為0ms,所以立即將其回撥函式timer1新增到延遲執行佇列的尾部
- 遇到Promise的executor,直接執行,依次輸出0、1、2、3、4,遇到resolve,將其回撥函式新增到當前的微任務佇列中
- 遇到console.log,直接輸出 “--結束--”
- 檢查微任務佇列,發現一個微任務,立即執行,輸出“Promise”
第二輪迴圈:普通訊息佇列為空,延遲執行佇列中有一個巨集任務(timer1)
- 執行timer1的回撥函式
- 發現微任務佇列為空,本輪迴圈結束
例題2:
new Promise((resolve) => {
console.log('this is executor');
resolve();
}).then(() => {
console.log('this is resolve');
setTimeout(() => { // 命名為timer1
console.log('timer1');
}, 4)
})
setTimeout(() => { // 命名為timer2
console.log('timer2');
}, 4)
console.log('-- 結束--');
//this is executor
//-- 結束--
//this is resolve
//timer2
//timer1
第一輪迴圈:普通訊息佇列中只有一個巨集任務(整個script)
- 遇到Promise的executor,直接執行,輸出“this is executor”
- 遇到resolve,將其回撥函式新增到當前的微任務佇列中
- 遇到setTimeout,在4ms後將timer2新增到延遲執行佇列的尾部
- 遇到console.log,直接輸出 “--結束--”
- 檢查微任務佇列,發現一個微任務,立即執行
- 遇到console.log,直接輸出 “this is resolve”
- 遇到setTimeout,在4ms後將timer1新增到延遲執行佇列的尾部,本輪迴圈結束
第二輪迴圈:普通訊息佇列為空,延遲執行佇列頭部為timer2
- 執行timer2,輸出timer2
第三輪迴圈:延遲執行佇列頭部為timer1
- 執行timer1,輸出timer1