javascript是一門單線程的非阻塞的腳本語言。單線程意味着javascript在執行代碼的任什麼時候候,都只有一個主線程來處理全部的任務。javascript
那麼javascript引擎是如何實現這一點的呢?java
由於事件循環(event loop)。先上圖:git
圖片解讀:github
Event Table
並註冊函數Event Table
會將這個函數移入Event Queue
中Event Queue
讀取對應的函數,進入主線程執行Event Loop(事件循環)
。咱們來一個簡單的例子來講明下:promise
console.log('1');
setTimeout(() => {
console.log('2');
}, 0)
console.log('3');
複製代碼
上面的代碼將輸出下面的結果:bash
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');
複製代碼
上面的代碼輸出的結果是:oop
1
4
5
7
6
3
2
複製代碼
看到這代碼的時候是否是有些蒙圈?在咱們揭開謎底以前,先來了解下微任務和宏任務。post
微任務和宏任務都是異步的任務,他們都屬於隊列,主要區別是它們的執行順序--微任務會比宏任務先執行。
宏任務包含有: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
複製代碼
客官能夠畫下圖整理下思路,而後代碼運行驗證一下啊💨
文章首發javascript事件循環機制,更多的內容,請戳個人博客進行了解,能留個star就更好了💨