天天看點

淺談JavaScript事件循環機制EventLoop

今天簡單說下js的事件循環機制,我們都知道,javascript是單線程語言,它的核心,也是因為它的單線程。

有很多小白不清楚EventLoop到底是什麼,按照中文翻譯,就是事件循環,那js到底是怎樣将同步和異步進行處理的。

這篇文章,就簡單說一說,js的單線程處理,也就是同步和異步的代碼是怎樣走向的。

一、同步和異步:

所有的線程,都是有同步隊列,和異步隊列,立即執行的任務隊列,這些都是屬于同步任務,比如一個簡單的函數;那麼像請求接口發送ajax,發送promise,或時間計時器等等,這些就是異步任務。

二、任務隊列-事件循環:

同步任務會立刻執行,進入到主線程當中,異步任務會被放到任務隊列(Event Queue)當中。

Event Queue 單詞的意思就是任務隊列。

等待同步代碼執行完畢後,傳回來,再将異步中的任務放到主線程中執行,反複這樣的循環,這就是事件循環。

也就是先執行同步,傳回來按照異步的順序再次執行

淺談JavaScript事件循環機制EventLoop
我們看下面這個代碼會列印出什麼:

console.log('開始111');

setTimeout(function() {

  console.log('setTimeout111');

}, 0);

Promise.resolve().then(function() {

  console.log('promise111');

}).then(function() {

  console.log('promise222');

});

console.log('開始222');           

我們猜想一下上面的代碼,會怎樣列印?我們知道,肯定是先走同步的代碼,從上往下,先列印 “開始111”,再列印“開始222”。

中途的三個異步,進入到了異步隊列,等待同步執行完(列印完),傳回來再執行異步,是以是後列印出來。

列印的結果先放一放,我們稍後回來再說。現在我們中途插播一段知識點:

三、宏觀任務和微觀任務(先執行微觀任務,再執行宏觀任務):

在事件循環中,每進行一次循環操作稱為tick,tick 的任務處理模型是比較複雜的,裡邊有兩個詞:分别是 Macro Task (宏任務)和 Micro Task(微任務)。

簡單來說:

宏觀任務主要包含:setTimeout、setInterval、script(整體代碼)、I/O、UI 互動事件、setImmediate(Node.js 環境)

微觀任務主要包括:Promise、MutaionObserver、process.nextTick(Node.js 環境)

規範:先執行微觀任務,再執行宏觀任務

那麼我們知道了,Promise 屬于微觀任務, setTimeout、setInterval 屬于宏觀任務,先執行微觀任務,等微觀任務執行完,再執行宏觀任務。是以我們再看一下這個代碼:

console.log('開始111');

setTimeout(function() {

  console.log('setTimeout111');

}, 0);

Promise.resolve().then(function() {

  console.log('promise111');

}).then(function() {

  console.log('promise222');

});

console.log('開始222');           

我們按照步驟來分析下:

1、遇到同步任務,直接先列印 “開始111”。

2、遇到異步 setTimeout ,先放到隊列中等待執行。

3、遇到了 Promise ,放到等待隊列中。

4、遇到同步任務,直接列印 “開始222”。

5、同步執行完,傳回執行隊列中的代碼,從上往下執行,發現有宏觀任務 setTimeout 和微觀任務 Promise ,那麼先執行微觀任務,再執行宏觀任務。

是以列印的順序為: 開始111 、開始222 、 promise111 、 promise222 、 setTimeout111 。

同理,我們再來分析一個代碼:

console.log('開始111');

setTimeout(function () {

  console.log('timeout111');

});

new Promise(resolve => {

  console.log('promise111');

  resolve();

  setTimeout(() => console.log('timeout222'));

}).then(function () {

  console.log('promise222')

})

console.log('開始222');           

分析一下:

1、遇到同步代碼,先列印 “開始111” 。

2、遇到setTimeout異步,放入隊列,等待執行 。

3、中途遇到Promise函數,函數直接執行,列印 “promise111”。

4、遇到setTimeout ,屬于異步,放入隊列,等待執行。

5、遇到Promise的then等待成功傳回,異步,放入隊列。

6、遇到同步,列印 “開始222”。

7、執行完,傳回,将異步隊列中的代碼,按順序執行。有一個微觀任務,then後的,是以列印 “promise222”,再執行兩個宏觀任務 “timeout111” “timeout222”。

是以,列印的順序為:開始111 、 promise111 、 開始222 、 promise222 、 timeout111 、 timeout222 .

先執行主任務,把異步任務放入循環隊列當中,等待主任務執行完,再執行隊列中的異步任務。異步任務先執行微觀任務,再執行宏觀任務。一直這樣循環,反複執行,就是事件循環機制。

擴充【宏觀任務和微觀任務】:

在宿主(如浏覽器)中發起的任務是宏觀任務,如setTimeout,js引擎中發起的任務是微觀任務,如Promise。js執行時,微觀任務優先于宏觀任務。

繼續閱讀