本文同步本人掘金平台的文章:https://juejin.cn/post/6844903887099412488
javascript是一門單線程的非阻塞的腳本語言。單線程意味着javascript在執行代碼的任何時候,都隻有一個主線程來處理所有的任務。
那麼javascript引擎是如何實作這一點的呢?
因為事件循環(event loop)。先上圖:
圖檔解讀:
- 同步和異步任務分别進入不同的執行場所,同步的進入主線程,異步的進入Event Table并注冊函數
- 當指定的事情完成時(重點),Event Table會将這個函數移入Event Queue中
- 主線程内的任務執行完畢為空,會去Event Queue讀取對應的函數,進入主線程執行
- 上述的過程會不斷的重複,也就是常常說的Event Loop(事件循環)。
簡單例子
我們來一個簡單的例子來說明下:
console.log('1');
setTimeout(() => {
console.log('2');
}, 0)
console.log('3');
複制代碼
上面的代碼将輸出下面的結果:
1
3
2
複制代碼
因為setTimeout是一個異步的任務,是以會在最後才執行。
那麼,我們來個複雜點的例子:
複雜例子
console.log('1');
setTimeout(() => {
console.log('2')
}, 1000);
new Promise((resolve, reject) => {
setTimeout(() => {
console.log('3');
}, 0);
console.log('4');
resolve();
console.log('5');
}).then(() => {
console.log('6');
});
console.log('7');
複制代碼
上面的代碼輸出的結果是:
1
4
5
7
6
3
2
複制代碼
看到這代碼的時候是不是有些蒙圈?在我們揭開謎底之前,先來了解下微任務和宏任務。
微任務和宏任務
微任務和宏任務都是異步的任務,他們都屬于隊列,主要差別是它們的執行順序--微任務會比宏任務先執行。
宏任務包含有:setTimeout, setInterval, setImmediate, I/O, UI rendering
微任務包含有:process.nextTick, promise.then, MutationObserver
嗯~回到上面的代碼,如下:
console.log('1');
setTimeout(() => {
console.log('2')
}, 1000);
new Promise((resolve, reject) => {
setTimeout(() => {
console.log('3');
}, 0);
console.log('4');
resolve();
console.log('5');
}).then(() => {
console.log('6');
});
console.log('7');
複制代碼
在執行到new Promise的時候會立馬建立一個promise對象并立即執行。是以會輸出 1,4,5,而then則會在Event Table中注冊成回調函數并放在微任務隊列中,而兩個setTimeout(輸出3)和setTimeout(輸出2,1s後完成的啊)會被先後注冊成回調函數并放在宏任務隊列中。
了解了上面的一些原理之後,我們再來練下手...
console.log(1)
process.nextTick(() => {
console.log(8)
setTimeout(() => {
console.log(9)
})
})
setTimeout(() => {
console.log(2)
new Promise(() => {
console.log(11)
})
})
let promise = new Promise((resolve,reject) => {
setTimeout(() => {
console.log(10)
})
resolve()
console.log(4)
})
fn()
console.log(3)
promise.then(() => {
console.log(12)
})
function fn(){
console.log(6)
}
複制代碼
得到的結果是:
1
4
6
3
8
12
2
11
10
9
複制代碼
客官可以畫下圖整理下思路,然後代碼運作驗證一下啊