如果面試官問你 JS中的event loop
是什么?我相信大多數人都能答出來JS是單線程語言,只有一個主線程執行,執行棧,同步、異步之類。但是,這樣的理解只是淺層的,如果面試官要你再深入解釋,我相信大多數人都卡住了,不知道還能解釋什么,那么,問題來了,這種情況怎么辦?
來看這篇文章,看完了你就知道怎么辦了。
JS頁面的任務不僅可以按照同步異步來分,也可以分為macro-task
和micro-task
macro-task都有:包括整體代碼script,setTimeout,setInterval
micro-task都有:Promise,process.nextTick
頁面初始化時,不同的任務會進入到不同的 Event Queue(事件隊列)
初始化時,會執行所有代碼,將setTimeout等加入到macro-task
的事件列表,將Promise等加入到micro-task
事件列表。
遇到立即執行的代碼,則立即執行,之后,會將事件列表中所有的micro-task
都執行完畢。
第一輪事件循環結束。
第二輪事件循環開始,拿出第一輪中第一個放入macro-task
列表中的事件開始執行完畢
第二輪事件循環結束。
第三輪循環開始,拿出第一輪中第二個放入macro-task列表中的事件開始執行完畢
第三輪事件循環結束。
就像上面這樣,反復執行,即事件循環。
看了上面的解釋,接下來,我們來看一個例子:
console.log('1');
setTimeout(function() {
console.log('2');
process.nextTick(function() {
console.log('3');
})
new Promise(function(resolve) {
console.log('4');
resolve();
}).then(function() {
console.log('5')
})
})
process.nextTick(function() {
console.log('6');
})
new Promise(function(resolve) {
console.log('7');
resolve();
}).then(function() {
console.log('8')
})
setTimeout(function() {
console.log('9');
process.nextTick(function() {
console.log('10');
})
new Promise(function(resolve) {
console.log('11');
resolve();
}).then(function() {
console.log('12')
})
})
我們按照上面的理論來分析這段代碼:
首先,進入到第一輪事件循環:
遇到 console.log('1') 輸出 '1'
遇到 setTimeout,將其回調函數加入到 macro-task
事件列表,記為 setTimeout1
遇到 process.nextTick,將其回調函數加入到 micro-task
事件列表,記為 process1
遇到 new Promise,立即執行,輸出 ** '7'**,then被分發到 micro-task
事件列表,記為 then1
遇到 setTimeout 將其回調函數加入到 macro-task
事件列表,記為 setTimeout2
執行到這里,我們來數數第一輪事件循環中的任務列表:
macro-task:setTimeout1,setTimeout2
micro-task:process1, then1
將列表中所有的 micro-task 執行完畢,輸出 '6','8'
第一輪事件循環正式結束。
第二輪事件循環開始:
這時,第一輪輸出結果是 '1','7','6','8'
在 macro-task
中,拿出第一個進入的事件,即setTimeout1,將其推入到執行棧開始執行
先輸出:'2'
遇到 process.nextTick,將其回調函數加入到 micro-task
事件列表,記為 process2
遇到 new Promise, 立即執行,輸出 ** '4'**,將then回調函數加入到 micro-task
事件列表,記為 then2
執行到這里,我們數數第二輪事件循環中的任務列表:
macro-task:setTimeout1
micro-task:process2,then2
將列表中所有的 micro-task 執行完畢,輸出:'3', '5'
第二輪事件循環結束
第三輪事件循環開始,前兩輪輸出為:'1','7','6','8','2','4','3', '5'
在 macro-task 中,拿出第二個進入的事件,即setTimeout2,將其推入到執行棧開始執行
先輸出:'9'
遇到 process.nextTick,將其回調函數加入到 micro-task
事件列表,記為 process3
遇到 new Promise, 立即執行,輸出 '11',將then回調函數加入到 micro-task
事件列表,記為 then3
執行到這里,我們數數第三輪事件循環中的任務列表:
macro-task:setTimeout2
micro-task:process3,then3
將列表中所有的 micro-task
執行完畢,輸出:'10', '12'
第三輪事件循環結束。
如果還有第四輪,第五輪,則循環上面的步驟,這樣的循環,即事件循環。
總結執行結果:'1','7','6','8','2','4','3','5','9','11','10','12'
好,寫完收工,等待下班去吃大餐。
原文作者寫的更好,大家可以看看 https://juejin.im/post/59e85eebf265da430d571f89