最近看了很多關於JS中事件循環機制的介紹,很多大佬寫的都非常的詳細,在此簡單記錄下我個人的理解,以下是個人見解,如果有錯誤,你來打。。。不是,希望指正。
Event Loop 是什么
JavaScript的事件分兩種,宏任務(macro-task)和微任務(micro-task)
-
宏任務:包括整體代碼script,setTimeout,setInterval
-
微任務:Promise.then(非new Promise),process.nextTick(node中)
-
事件的執行順序,
是先執行宏任務,然后執行微任務,這個是基礎,任務可以有同步任務和異步任務,同步的進入主線程,異步的進入Event Table並注冊函數,異步事件完成后,會將回調函數放入Event Queue中(宏任務和微任務是不同的Event Queue),同步任務執行完成后,會從Event Queue中讀取事件放入主線程執行,回調函數中可能還會包含不同的任務,因此會循環執行上述操作。
setTimeout(() => {
console.log('延時1秒');
},1000)
console.log("開始")
輸出:
開始
延時1秒
上述代碼,setTimeout函數是宏任務,且是異步任務,因此會將函數放入Event Table並注冊函數,經過指定時間后,把要執行的任務加入到Event Queue中,等待同步任務console.log("開始")執行結束后,讀取Event Queue中setTimeout的回調函數執行。
上述代碼不包含微任務,接下來看包含微任務的代碼:
setTimeout(function() {
console.log('setTimeout');
},1000)
new Promise(function(resolve) {
console.log('promise');
}).then(function() {
console.log('then');
})
console.log('console');
- 首先setTimeout,放入Event Table中,1秒后將回調函數放入宏任務的Event Queue中
- new Promise 同步代碼,立即執行
console.log('promise'),然后看到微任務then,因此將其放入微任務的Event Queue中 - 接下來執行同步代碼
console.log('console') - 主線程的宏任務,已經執行完畢,接下來要執行微任務,因此會執行Promise.then,到此,第一輪事件循環執行完畢
- 第二輪事件循環開始,先執行宏任務,即setTimeout的回調函數,然后查找是否有微任務,沒有,時間循環結束
到此做個總結,事件循環,先執行宏任務,其中同步任務立即執行,異步任務,加載到對應的的Event Queue中(setTimeout等加入宏任務的Event Queue,Promise.then加入微任務的Event Queue),所有同步宏任務執行完畢后,如果發現微任務的Event Queue中有未執行的任務,會先執行其中的任務,這樣算是完成了一次事件循環。接下來查看宏任務的Event Queue中是否有未執行的任務,有的話,就開始第二輪事件循環,依此類推。
上述例子只是簡單的一層嵌套,接下來看一個稍微復雜了一點點的例子:
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')
})
})
輸出:
1
2
4
3
5
- 宏任務同步代碼
console.log('1'),不多說 - setTimeout,加入宏任務Event Queue,沒有發現微任務,第一輪事件循環走完
- 第二輪事件循環開始,先執行宏任務,從宏任務Event Queue中獨取出setTimeout的回調函數
- 同步代碼
console.log('2'),發現process.nextTick,加入微任務Event Queue - new Promise,同步執行
console.log('4'),發現then,加入微任務Event Queue - 宏任務執行完畢,接下來執行微任務,先執行process.nextTick,然后執行Promise.then
- 微任務執行完畢,第二輪事件循環走完,沒有發現宏任務,事件循環結束
以上就是看完一些介紹的文章后的一點個人理解,大量參考了掘金一位大佬ssssyoki的文章。
