天天看點

JavaScript 事件循環機制

作者:吉米龐

本文同步本人掘金平台的文章:https://juejin.cn/post/6844903887099412488

javascript是一門單線程的非阻塞的腳本語言。單線程意味着javascript在執行代碼的任何時候,都隻有一個主線程來處理所有的任務。

那麼javascript引擎是如何實作這一點的呢?

因為事件循環(event loop)。先上圖:

JavaScript 事件循環機制

圖檔解讀:

  • 同步和異步任務分别進入不同的執行場所,同步的進入主線程,異步的進入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
複制代碼           

客官可以畫下圖整理下思路,然後代碼運作驗證一下啊

繼續閱讀