說到 event loop,不得不提的是: JavaScript 是單線程的。即一次只能作一件事情。Event Loop 就是讓 JavaScript 在運行時具備併發的能力。javascript
// global sync task function add(a, b) { return a + b; } // add sync task const sum = add(); // bar async task setTimeout(function bar() { console.log('bar called!'); }, 1000); // fetchUsers async task fetch('https://www.example.com/api/v1/users').then(function fetchUsers(res) { console.log({ res }); });
global task
和 foo task
,包含的異步任務有 bar task
要想理解 Event Loop,就得先了解 JavaScript 中的 執行棧
(JavaScript execution context stack)java
JavaScript 是單線程的,只有一個主線程。在這個主線程中,有一個棧。git
在每一個函數 運行時
,會生成一個 執行上下文(execution context)
,這個執行上下文包含了當前函數運行時的參數,局部變量等信息。github
開始執行函數
時,執行上下文會被 推到執行棧中
;函數執行完畢後
,這個 執行上下文又會從棧中彈出
。api
例子promise
function foo() { console.log('foo'); } function bar() { console.log('bar'); foo(); } bar();
global 執行上下文
被推入 執行棧
中bar()
函數開始運行,bar() 函數執行上下文
被推入 執行棧
中foo()
函數開始運行,foo() 函數執行上下文
被推入 執行棧
中foo()
函數執行完畢,彈出bar()
函數執行完畢,彈出總結:執行棧
至關於一條流水線,全部的 同步任務
運行時
,都會被放到這條流水線上去一個一個 按照順序(函數調用順序)
執行。網絡
既然 同步任務運行時
是被放到 執行棧
中,那 異步任務運行時以及定義時
是被放到哪裏呢?併發
在異步任務中,又分爲兩類任務:異步
要回答上面這個問題,首先了解一下 macrotask
和 microtask
。async
如下派發的任務就是 macrotask
每個 Event Loop 都有一個 macrotask 隊列
:
setTimeout(function foo() { console.log('foo'); }, 1000); setTimeout(function bar() { console.log('foo'); }, 2000);
foo()
macrotask 在 1000ms 後被推入 macrotask 隊列
中bar()
macrotask 在 2000ms 後被推入 macrotask 隊列
中如下派發的任務時 microtask:
每個 Event Loop 都有一個 microtask 隊列
:
// 調用 Promise 構造函數時,傳入的回調是會當即調用的,因此 foo 不算 microtask // new Promise(function foo(resolve) { // console.log('foo'); // resolve(); // }); // 調用 Promise 構造函數時,傳入的回調是會當即調用的,因此 bar 不算 microtask // new Promise(function bar(resolve) { // console.log('bar'); // resolve(); // }); Promise.resolve() .then(function foo() { console.log('promise1'); }) .then(function bar() { console.log('promise2'); });
foo()
microtask 被推入 microtask 隊列
中bar()
microtask 被推入 microtask 隊列
中因此,如今來看看上面的問題:
異步任務
運行時
以及 定義時
是被放到哪裏呢?異步任務
屬於 macrotask
,則在 定義時
被放到 macrotask 隊列
中;若是 異步任務
屬於 microtask
,則在 定義時
被放到 microtask 隊列
中上面只回答了 異步任務
定義時
被放置的位置。接下來經過 Event Loop 來回答 異步任務
在 運行時
會在哪裏。
上面講了:
代碼層面
,存在 定義狀態
和 執行狀態
。在 執行狀態
時被推入 執行棧
中執行異步任務:異步任務在 代碼層面
,只存在 定義狀態
。異步任務在 定義時
,根據異步任務的類型,被推入不一樣的隊列中
運行時
被推入到 執行棧
中執行但上面還留下了一個問題:異步任務
在 運行時
會在哪裏?
這個時候,就須要知道 Event Loop 了。
Event Loop 翻譯成中文爲 事件循環
。循環
二字,就說明了 Event Loop 表示是一個在不斷 循環
的過程。
這個 循環
包含了如下過程:
執行棧
中的全部任務是否所有執行完畢了。若未執行完,則繼續執行;不然進入下一步microtask 隊列
是否存在 microtask
。若存在 microtask
,則將 microtask 隊列
中的全部 microtask
都推入 執行棧
中執行(不然進入下一步)。執行完畢後,進入下一步macrotask 隊列
中是否存在 macrotask
。若存在 macrotask
,則將 macrotask 隊列
中的 第一個
出隊,推入到 執行棧
中執行(不然進入第 1 步,一個 Event Loop 完成)。執行完畢後,進入第 1 步(一個 Event Loop 完成)
因此,如今能夠回答上面的問題了:異步任務
在 運行時
會通過 Event Loop,且被推入到 執行棧
中。
Event Loop(事件循環)就是一個對 異步任務
進行 協調的過程