'js執行機制:event loop(多圖理解)'

腳本語言 Node.js K前端 2019-07-17
"

是什麼

我們都知道js是單線程運行,那麼在單線程進程中具體如果執行js的腳本,你或許還不清晰,那麼該文章你就得看下去了。

js腳本(任務)的執行遵循一個機制:event loop,中文稱為“事件循環”吧。顧名思義,表示的是周而復始不斷循環執行任務事件的一個過程機制

這個機制主要是為了實現如何在js單線程運行的大背景下實現異步,避免出現腳本阻塞的情況。

在說明這個機制之前,我們先了解一下基礎的概念,方便之後的理解

任務的區分

同步與異步任務

我們知道任務可以劃分為同步任務和異步任務,我這裡不說具體標準的定義,通俗說一下其意思。

同步任務:能馬上被執行的,按照一定順序依次被執行的任務

異步任務:等到一定觸發時機發生了,才會被執行對應的操作,並不是馬上就執行

宏任務與微任務

  • macro-task(宏任務):

整體的script、setTimeout、setInterval、I/O、ui渲染、setImmediate

以上腳本操作產生的是宏任務,可能有些資料把宏任務稱呼為task

  • micro-task(微任務):

Promise的方法,如then等、process.nextTick(Node.js)、MutationObserve

注意,在微任務中,process.nextTick的觸發順序會比其他微任務要先執行

以上腳本操作產生的是微任務

任務執行流程

js是單線程運行的,只有一個進程在執行著腳本任務,即主線程,那什麼任務才會進入主線程呢,這是由一個叫call stack(調用棧/執行棧)的傢伙來處理的。

從宏觀上看,event loop就是這麼一個循環


"

是什麼

我們都知道js是單線程運行,那麼在單線程進程中具體如果執行js的腳本,你或許還不清晰,那麼該文章你就得看下去了。

js腳本(任務)的執行遵循一個機制:event loop,中文稱為“事件循環”吧。顧名思義,表示的是周而復始不斷循環執行任務事件的一個過程機制

這個機制主要是為了實現如何在js單線程運行的大背景下實現異步,避免出現腳本阻塞的情況。

在說明這個機制之前,我們先了解一下基礎的概念,方便之後的理解

任務的區分

同步與異步任務

我們知道任務可以劃分為同步任務和異步任務,我這裡不說具體標準的定義,通俗說一下其意思。

同步任務:能馬上被執行的,按照一定順序依次被執行的任務

異步任務:等到一定觸發時機發生了,才會被執行對應的操作,並不是馬上就執行

宏任務與微任務

  • macro-task(宏任務):

整體的script、setTimeout、setInterval、I/O、ui渲染、setImmediate

以上腳本操作產生的是宏任務,可能有些資料把宏任務稱呼為task

  • micro-task(微任務):

Promise的方法,如then等、process.nextTick(Node.js)、MutationObserve

注意,在微任務中,process.nextTick的觸發順序會比其他微任務要先執行

以上腳本操作產生的是微任務

任務執行流程

js是單線程運行的,只有一個進程在執行著腳本任務,即主線程,那什麼任務才會進入主線程呢,這是由一個叫call stack(調用棧/執行棧)的傢伙來處理的。

從宏觀上看,event loop就是這麼一個循環


js執行機制:event loop(多圖理解)


從宏任務隊列中找到最開始的任務,此刻是有微任務,如果有,則執行完畢後執行此刻所有的微任務,執行完重新回到宏任務隊列中找最開始的任務執行(原先的最開始任務已經出隊了,原本排第二的任務現變成最開始的任務了);若無,也是重新回到宏任務隊列中找最開始的任務執行;然後按照剛才的順序再循環一遍。

以上這個循環不斷周而復始的過程就是event loop了

那麼,從什麼時候就已經劃分好宏任務和微任務呢,即宏任務和微任務的隊列是如何產生呢?看下面這個圖先大體感受一下


"

是什麼

我們都知道js是單線程運行,那麼在單線程進程中具體如果執行js的腳本,你或許還不清晰,那麼該文章你就得看下去了。

js腳本(任務)的執行遵循一個機制:event loop,中文稱為“事件循環”吧。顧名思義,表示的是周而復始不斷循環執行任務事件的一個過程機制

這個機制主要是為了實現如何在js單線程運行的大背景下實現異步,避免出現腳本阻塞的情況。

在說明這個機制之前,我們先了解一下基礎的概念,方便之後的理解

任務的區分

同步與異步任務

我們知道任務可以劃分為同步任務和異步任務,我這裡不說具體標準的定義,通俗說一下其意思。

同步任務:能馬上被執行的,按照一定順序依次被執行的任務

異步任務:等到一定觸發時機發生了,才會被執行對應的操作,並不是馬上就執行

宏任務與微任務

  • macro-task(宏任務):

整體的script、setTimeout、setInterval、I/O、ui渲染、setImmediate

以上腳本操作產生的是宏任務,可能有些資料把宏任務稱呼為task

  • micro-task(微任務):

Promise的方法,如then等、process.nextTick(Node.js)、MutationObserve

注意,在微任務中,process.nextTick的觸發順序會比其他微任務要先執行

以上腳本操作產生的是微任務

任務執行流程

js是單線程運行的,只有一個進程在執行著腳本任務,即主線程,那什麼任務才會進入主線程呢,這是由一個叫call stack(調用棧/執行棧)的傢伙來處理的。

從宏觀上看,event loop就是這麼一個循環


js執行機制:event loop(多圖理解)


從宏任務隊列中找到最開始的任務,此刻是有微任務,如果有,則執行完畢後執行此刻所有的微任務,執行完重新回到宏任務隊列中找最開始的任務執行(原先的最開始任務已經出隊了,原本排第二的任務現變成最開始的任務了);若無,也是重新回到宏任務隊列中找最開始的任務執行;然後按照剛才的順序再循環一遍。

以上這個循環不斷周而復始的過程就是event loop了

那麼,從什麼時候就已經劃分好宏任務和微任務呢,即宏任務和微任務的隊列是如何產生呢?看下面這個圖先大體感受一下


js執行機制:event loop(多圖理解)


上面的圖我畫的較其他資料都要仔細點,是個詳細版的事件循環流程圖。

我們可以看到,在異步操作的觸發時機發生時,就會把任務入隊,入哪個隊呢,就按照我上面說的,是什麼樣的腳本產生就歸屬哪類(如setTimeout產生的是宏任務)

這裡的event table可以理解成是一個註冊機構,用來註冊異步操作的回調函數的。

因為我們說了,整體script腳本也算是一個宏任務,所以進入頁面,執行最初始的腳本時就算是開始了從宏任務開頭的事件循環這麼一個過程了。

如果上面的圖你還是不太理解,或許你需要了解一下call stack的一些原理。

call stack原理

這裡有一份叫main.js的代碼:

 var firstFunction = function(){
console.log("I'm first!");
};
var secondFunction = function(){
firstFunction();
console.log("I'm second!");
};
secondFunction();

以這份代碼為例子舉例說明。

  • 當第一次執行main.js時,調用棧的初始初始狀態是這樣的(上也說了,script腳本算是一個宏任務):


"

是什麼

我們都知道js是單線程運行,那麼在單線程進程中具體如果執行js的腳本,你或許還不清晰,那麼該文章你就得看下去了。

js腳本(任務)的執行遵循一個機制:event loop,中文稱為“事件循環”吧。顧名思義,表示的是周而復始不斷循環執行任務事件的一個過程機制

這個機制主要是為了實現如何在js單線程運行的大背景下實現異步,避免出現腳本阻塞的情況。

在說明這個機制之前,我們先了解一下基礎的概念,方便之後的理解

任務的區分

同步與異步任務

我們知道任務可以劃分為同步任務和異步任務,我這裡不說具體標準的定義,通俗說一下其意思。

同步任務:能馬上被執行的,按照一定順序依次被執行的任務

異步任務:等到一定觸發時機發生了,才會被執行對應的操作,並不是馬上就執行

宏任務與微任務

  • macro-task(宏任務):

整體的script、setTimeout、setInterval、I/O、ui渲染、setImmediate

以上腳本操作產生的是宏任務,可能有些資料把宏任務稱呼為task

  • micro-task(微任務):

Promise的方法,如then等、process.nextTick(Node.js)、MutationObserve

注意,在微任務中,process.nextTick的觸發順序會比其他微任務要先執行

以上腳本操作產生的是微任務

任務執行流程

js是單線程運行的,只有一個進程在執行著腳本任務,即主線程,那什麼任務才會進入主線程呢,這是由一個叫call stack(調用棧/執行棧)的傢伙來處理的。

從宏觀上看,event loop就是這麼一個循環


js執行機制:event loop(多圖理解)


從宏任務隊列中找到最開始的任務,此刻是有微任務,如果有,則執行完畢後執行此刻所有的微任務,執行完重新回到宏任務隊列中找最開始的任務執行(原先的最開始任務已經出隊了,原本排第二的任務現變成最開始的任務了);若無,也是重新回到宏任務隊列中找最開始的任務執行;然後按照剛才的順序再循環一遍。

以上這個循環不斷周而復始的過程就是event loop了

那麼,從什麼時候就已經劃分好宏任務和微任務呢,即宏任務和微任務的隊列是如何產生呢?看下面這個圖先大體感受一下


js執行機制:event loop(多圖理解)


上面的圖我畫的較其他資料都要仔細點,是個詳細版的事件循環流程圖。

我們可以看到,在異步操作的觸發時機發生時,就會把任務入隊,入哪個隊呢,就按照我上面說的,是什麼樣的腳本產生就歸屬哪類(如setTimeout產生的是宏任務)

這裡的event table可以理解成是一個註冊機構,用來註冊異步操作的回調函數的。

因為我們說了,整體script腳本也算是一個宏任務,所以進入頁面,執行最初始的腳本時就算是開始了從宏任務開頭的事件循環這麼一個過程了。

如果上面的圖你還是不太理解,或許你需要了解一下call stack的一些原理。

call stack原理

這裡有一份叫main.js的代碼:

 var firstFunction = function(){
console.log("I'm first!");
};
var secondFunction = function(){
firstFunction();
console.log("I'm second!");
};
secondFunction();

以這份代碼為例子舉例說明。

  • 當第一次執行main.js時,調用棧的初始初始狀態是這樣的(上也說了,script腳本算是一個宏任務):


js執行機制:event loop(多圖理解)


  • 當調用secondFunction時,此刻調用棧情況:


"

是什麼

我們都知道js是單線程運行,那麼在單線程進程中具體如果執行js的腳本,你或許還不清晰,那麼該文章你就得看下去了。

js腳本(任務)的執行遵循一個機制:event loop,中文稱為“事件循環”吧。顧名思義,表示的是周而復始不斷循環執行任務事件的一個過程機制

這個機制主要是為了實現如何在js單線程運行的大背景下實現異步,避免出現腳本阻塞的情況。

在說明這個機制之前,我們先了解一下基礎的概念,方便之後的理解

任務的區分

同步與異步任務

我們知道任務可以劃分為同步任務和異步任務,我這裡不說具體標準的定義,通俗說一下其意思。

同步任務:能馬上被執行的,按照一定順序依次被執行的任務

異步任務:等到一定觸發時機發生了,才會被執行對應的操作,並不是馬上就執行

宏任務與微任務

  • macro-task(宏任務):

整體的script、setTimeout、setInterval、I/O、ui渲染、setImmediate

以上腳本操作產生的是宏任務,可能有些資料把宏任務稱呼為task

  • micro-task(微任務):

Promise的方法,如then等、process.nextTick(Node.js)、MutationObserve

注意,在微任務中,process.nextTick的觸發順序會比其他微任務要先執行

以上腳本操作產生的是微任務

任務執行流程

js是單線程運行的,只有一個進程在執行著腳本任務,即主線程,那什麼任務才會進入主線程呢,這是由一個叫call stack(調用棧/執行棧)的傢伙來處理的。

從宏觀上看,event loop就是這麼一個循環


js執行機制:event loop(多圖理解)


從宏任務隊列中找到最開始的任務,此刻是有微任務,如果有,則執行完畢後執行此刻所有的微任務,執行完重新回到宏任務隊列中找最開始的任務執行(原先的最開始任務已經出隊了,原本排第二的任務現變成最開始的任務了);若無,也是重新回到宏任務隊列中找最開始的任務執行;然後按照剛才的順序再循環一遍。

以上這個循環不斷周而復始的過程就是event loop了

那麼,從什麼時候就已經劃分好宏任務和微任務呢,即宏任務和微任務的隊列是如何產生呢?看下面這個圖先大體感受一下


js執行機制:event loop(多圖理解)


上面的圖我畫的較其他資料都要仔細點,是個詳細版的事件循環流程圖。

我們可以看到,在異步操作的觸發時機發生時,就會把任務入隊,入哪個隊呢,就按照我上面說的,是什麼樣的腳本產生就歸屬哪類(如setTimeout產生的是宏任務)

這裡的event table可以理解成是一個註冊機構,用來註冊異步操作的回調函數的。

因為我們說了,整體script腳本也算是一個宏任務,所以進入頁面,執行最初始的腳本時就算是開始了從宏任務開頭的事件循環這麼一個過程了。

如果上面的圖你還是不太理解,或許你需要了解一下call stack的一些原理。

call stack原理

這裡有一份叫main.js的代碼:

 var firstFunction = function(){
console.log("I'm first!");
};
var secondFunction = function(){
firstFunction();
console.log("I'm second!");
};
secondFunction();

以這份代碼為例子舉例說明。

  • 當第一次執行main.js時,調用棧的初始初始狀態是這樣的(上也說了,script腳本算是一個宏任務):


js執行機制:event loop(多圖理解)


  • 當調用secondFunction時,此刻調用棧情況:


js執行機制:event loop(多圖理解)


  • 在secondFunction裡還調用了firstFunction,此刻調用棧:


"

是什麼

我們都知道js是單線程運行,那麼在單線程進程中具體如果執行js的腳本,你或許還不清晰,那麼該文章你就得看下去了。

js腳本(任務)的執行遵循一個機制:event loop,中文稱為“事件循環”吧。顧名思義,表示的是周而復始不斷循環執行任務事件的一個過程機制

這個機制主要是為了實現如何在js單線程運行的大背景下實現異步,避免出現腳本阻塞的情況。

在說明這個機制之前,我們先了解一下基礎的概念,方便之後的理解

任務的區分

同步與異步任務

我們知道任務可以劃分為同步任務和異步任務,我這裡不說具體標準的定義,通俗說一下其意思。

同步任務:能馬上被執行的,按照一定順序依次被執行的任務

異步任務:等到一定觸發時機發生了,才會被執行對應的操作,並不是馬上就執行

宏任務與微任務

  • macro-task(宏任務):

整體的script、setTimeout、setInterval、I/O、ui渲染、setImmediate

以上腳本操作產生的是宏任務,可能有些資料把宏任務稱呼為task

  • micro-task(微任務):

Promise的方法,如then等、process.nextTick(Node.js)、MutationObserve

注意,在微任務中,process.nextTick的觸發順序會比其他微任務要先執行

以上腳本操作產生的是微任務

任務執行流程

js是單線程運行的,只有一個進程在執行著腳本任務,即主線程,那什麼任務才會進入主線程呢,這是由一個叫call stack(調用棧/執行棧)的傢伙來處理的。

從宏觀上看,event loop就是這麼一個循環


js執行機制:event loop(多圖理解)


從宏任務隊列中找到最開始的任務,此刻是有微任務,如果有,則執行完畢後執行此刻所有的微任務,執行完重新回到宏任務隊列中找最開始的任務執行(原先的最開始任務已經出隊了,原本排第二的任務現變成最開始的任務了);若無,也是重新回到宏任務隊列中找最開始的任務執行;然後按照剛才的順序再循環一遍。

以上這個循環不斷周而復始的過程就是event loop了

那麼,從什麼時候就已經劃分好宏任務和微任務呢,即宏任務和微任務的隊列是如何產生呢?看下面這個圖先大體感受一下


js執行機制:event loop(多圖理解)


上面的圖我畫的較其他資料都要仔細點,是個詳細版的事件循環流程圖。

我們可以看到,在異步操作的觸發時機發生時,就會把任務入隊,入哪個隊呢,就按照我上面說的,是什麼樣的腳本產生就歸屬哪類(如setTimeout產生的是宏任務)

這裡的event table可以理解成是一個註冊機構,用來註冊異步操作的回調函數的。

因為我們說了,整體script腳本也算是一個宏任務,所以進入頁面,執行最初始的腳本時就算是開始了從宏任務開頭的事件循環這麼一個過程了。

如果上面的圖你還是不太理解,或許你需要了解一下call stack的一些原理。

call stack原理

這裡有一份叫main.js的代碼:

 var firstFunction = function(){
console.log("I'm first!");
};
var secondFunction = function(){
firstFunction();
console.log("I'm second!");
};
secondFunction();

以這份代碼為例子舉例說明。

  • 當第一次執行main.js時,調用棧的初始初始狀態是這樣的(上也說了,script腳本算是一個宏任務):


js執行機制:event loop(多圖理解)


  • 當調用secondFunction時,此刻調用棧情況:


js執行機制:event loop(多圖理解)


  • 在secondFunction裡還調用了firstFunction,此刻調用棧:


js執行機制:event loop(多圖理解)


  • 當執行完了firstFunction:


"

是什麼

我們都知道js是單線程運行,那麼在單線程進程中具體如果執行js的腳本,你或許還不清晰,那麼該文章你就得看下去了。

js腳本(任務)的執行遵循一個機制:event loop,中文稱為“事件循環”吧。顧名思義,表示的是周而復始不斷循環執行任務事件的一個過程機制

這個機制主要是為了實現如何在js單線程運行的大背景下實現異步,避免出現腳本阻塞的情況。

在說明這個機制之前,我們先了解一下基礎的概念,方便之後的理解

任務的區分

同步與異步任務

我們知道任務可以劃分為同步任務和異步任務,我這裡不說具體標準的定義,通俗說一下其意思。

同步任務:能馬上被執行的,按照一定順序依次被執行的任務

異步任務:等到一定觸發時機發生了,才會被執行對應的操作,並不是馬上就執行

宏任務與微任務

  • macro-task(宏任務):

整體的script、setTimeout、setInterval、I/O、ui渲染、setImmediate

以上腳本操作產生的是宏任務,可能有些資料把宏任務稱呼為task

  • micro-task(微任務):

Promise的方法,如then等、process.nextTick(Node.js)、MutationObserve

注意,在微任務中,process.nextTick的觸發順序會比其他微任務要先執行

以上腳本操作產生的是微任務

任務執行流程

js是單線程運行的,只有一個進程在執行著腳本任務,即主線程,那什麼任務才會進入主線程呢,這是由一個叫call stack(調用棧/執行棧)的傢伙來處理的。

從宏觀上看,event loop就是這麼一個循環


js執行機制:event loop(多圖理解)


從宏任務隊列中找到最開始的任務,此刻是有微任務,如果有,則執行完畢後執行此刻所有的微任務,執行完重新回到宏任務隊列中找最開始的任務執行(原先的最開始任務已經出隊了,原本排第二的任務現變成最開始的任務了);若無,也是重新回到宏任務隊列中找最開始的任務執行;然後按照剛才的順序再循環一遍。

以上這個循環不斷周而復始的過程就是event loop了

那麼,從什麼時候就已經劃分好宏任務和微任務呢,即宏任務和微任務的隊列是如何產生呢?看下面這個圖先大體感受一下


js執行機制:event loop(多圖理解)


上面的圖我畫的較其他資料都要仔細點,是個詳細版的事件循環流程圖。

我們可以看到,在異步操作的觸發時機發生時,就會把任務入隊,入哪個隊呢,就按照我上面說的,是什麼樣的腳本產生就歸屬哪類(如setTimeout產生的是宏任務)

這裡的event table可以理解成是一個註冊機構,用來註冊異步操作的回調函數的。

因為我們說了,整體script腳本也算是一個宏任務,所以進入頁面,執行最初始的腳本時就算是開始了從宏任務開頭的事件循環這麼一個過程了。

如果上面的圖你還是不太理解,或許你需要了解一下call stack的一些原理。

call stack原理

這裡有一份叫main.js的代碼:

 var firstFunction = function(){
console.log("I'm first!");
};
var secondFunction = function(){
firstFunction();
console.log("I'm second!");
};
secondFunction();

以這份代碼為例子舉例說明。

  • 當第一次執行main.js時,調用棧的初始初始狀態是這樣的(上也說了,script腳本算是一個宏任務):


js執行機制:event loop(多圖理解)


  • 當調用secondFunction時,此刻調用棧情況:


js執行機制:event loop(多圖理解)


  • 在secondFunction裡還調用了firstFunction,此刻調用棧:


js執行機制:event loop(多圖理解)


  • 當執行完了firstFunction:


js執行機制:event loop(多圖理解)


  • 同樣,執行完了secondFunction之後,就算執行完了main.js這個宏任務了,所以會有以下變化:


"

是什麼

我們都知道js是單線程運行,那麼在單線程進程中具體如果執行js的腳本,你或許還不清晰,那麼該文章你就得看下去了。

js腳本(任務)的執行遵循一個機制:event loop,中文稱為“事件循環”吧。顧名思義,表示的是周而復始不斷循環執行任務事件的一個過程機制

這個機制主要是為了實現如何在js單線程運行的大背景下實現異步,避免出現腳本阻塞的情況。

在說明這個機制之前,我們先了解一下基礎的概念,方便之後的理解

任務的區分

同步與異步任務

我們知道任務可以劃分為同步任務和異步任務,我這裡不說具體標準的定義,通俗說一下其意思。

同步任務:能馬上被執行的,按照一定順序依次被執行的任務

異步任務:等到一定觸發時機發生了,才會被執行對應的操作,並不是馬上就執行

宏任務與微任務

  • macro-task(宏任務):

整體的script、setTimeout、setInterval、I/O、ui渲染、setImmediate

以上腳本操作產生的是宏任務,可能有些資料把宏任務稱呼為task

  • micro-task(微任務):

Promise的方法,如then等、process.nextTick(Node.js)、MutationObserve

注意,在微任務中,process.nextTick的觸發順序會比其他微任務要先執行

以上腳本操作產生的是微任務

任務執行流程

js是單線程運行的,只有一個進程在執行著腳本任務,即主線程,那什麼任務才會進入主線程呢,這是由一個叫call stack(調用棧/執行棧)的傢伙來處理的。

從宏觀上看,event loop就是這麼一個循環


js執行機制:event loop(多圖理解)


從宏任務隊列中找到最開始的任務,此刻是有微任務,如果有,則執行完畢後執行此刻所有的微任務,執行完重新回到宏任務隊列中找最開始的任務執行(原先的最開始任務已經出隊了,原本排第二的任務現變成最開始的任務了);若無,也是重新回到宏任務隊列中找最開始的任務執行;然後按照剛才的順序再循環一遍。

以上這個循環不斷周而復始的過程就是event loop了

那麼,從什麼時候就已經劃分好宏任務和微任務呢,即宏任務和微任務的隊列是如何產生呢?看下面這個圖先大體感受一下


js執行機制:event loop(多圖理解)


上面的圖我畫的較其他資料都要仔細點,是個詳細版的事件循環流程圖。

我們可以看到,在異步操作的觸發時機發生時,就會把任務入隊,入哪個隊呢,就按照我上面說的,是什麼樣的腳本產生就歸屬哪類(如setTimeout產生的是宏任務)

這裡的event table可以理解成是一個註冊機構,用來註冊異步操作的回調函數的。

因為我們說了,整體script腳本也算是一個宏任務,所以進入頁面,執行最初始的腳本時就算是開始了從宏任務開頭的事件循環這麼一個過程了。

如果上面的圖你還是不太理解,或許你需要了解一下call stack的一些原理。

call stack原理

這裡有一份叫main.js的代碼:

 var firstFunction = function(){
console.log("I'm first!");
};
var secondFunction = function(){
firstFunction();
console.log("I'm second!");
};
secondFunction();

以這份代碼為例子舉例說明。

  • 當第一次執行main.js時,調用棧的初始初始狀態是這樣的(上也說了,script腳本算是一個宏任務):


js執行機制:event loop(多圖理解)


  • 當調用secondFunction時,此刻調用棧情況:


js執行機制:event loop(多圖理解)


  • 在secondFunction裡還調用了firstFunction,此刻調用棧:


js執行機制:event loop(多圖理解)


  • 當執行完了firstFunction:


js執行機制:event loop(多圖理解)


  • 同樣,執行完了secondFunction之後,就算執行完了main.js這個宏任務了,所以會有以下變化:


js執行機制:event loop(多圖理解)




"

是什麼

我們都知道js是單線程運行,那麼在單線程進程中具體如果執行js的腳本,你或許還不清晰,那麼該文章你就得看下去了。

js腳本(任務)的執行遵循一個機制:event loop,中文稱為“事件循環”吧。顧名思義,表示的是周而復始不斷循環執行任務事件的一個過程機制

這個機制主要是為了實現如何在js單線程運行的大背景下實現異步,避免出現腳本阻塞的情況。

在說明這個機制之前,我們先了解一下基礎的概念,方便之後的理解

任務的區分

同步與異步任務

我們知道任務可以劃分為同步任務和異步任務,我這裡不說具體標準的定義,通俗說一下其意思。

同步任務:能馬上被執行的,按照一定順序依次被執行的任務

異步任務:等到一定觸發時機發生了,才會被執行對應的操作,並不是馬上就執行

宏任務與微任務

  • macro-task(宏任務):

整體的script、setTimeout、setInterval、I/O、ui渲染、setImmediate

以上腳本操作產生的是宏任務,可能有些資料把宏任務稱呼為task

  • micro-task(微任務):

Promise的方法,如then等、process.nextTick(Node.js)、MutationObserve

注意,在微任務中,process.nextTick的觸發順序會比其他微任務要先執行

以上腳本操作產生的是微任務

任務執行流程

js是單線程運行的,只有一個進程在執行著腳本任務,即主線程,那什麼任務才會進入主線程呢,這是由一個叫call stack(調用棧/執行棧)的傢伙來處理的。

從宏觀上看,event loop就是這麼一個循環


js執行機制:event loop(多圖理解)


從宏任務隊列中找到最開始的任務,此刻是有微任務,如果有,則執行完畢後執行此刻所有的微任務,執行完重新回到宏任務隊列中找最開始的任務執行(原先的最開始任務已經出隊了,原本排第二的任務現變成最開始的任務了);若無,也是重新回到宏任務隊列中找最開始的任務執行;然後按照剛才的順序再循環一遍。

以上這個循環不斷周而復始的過程就是event loop了

那麼,從什麼時候就已經劃分好宏任務和微任務呢,即宏任務和微任務的隊列是如何產生呢?看下面這個圖先大體感受一下


js執行機制:event loop(多圖理解)


上面的圖我畫的較其他資料都要仔細點,是個詳細版的事件循環流程圖。

我們可以看到,在異步操作的觸發時機發生時,就會把任務入隊,入哪個隊呢,就按照我上面說的,是什麼樣的腳本產生就歸屬哪類(如setTimeout產生的是宏任務)

這裡的event table可以理解成是一個註冊機構,用來註冊異步操作的回調函數的。

因為我們說了,整體script腳本也算是一個宏任務,所以進入頁面,執行最初始的腳本時就算是開始了從宏任務開頭的事件循環這麼一個過程了。

如果上面的圖你還是不太理解,或許你需要了解一下call stack的一些原理。

call stack原理

這裡有一份叫main.js的代碼:

 var firstFunction = function(){
console.log("I'm first!");
};
var secondFunction = function(){
firstFunction();
console.log("I'm second!");
};
secondFunction();

以這份代碼為例子舉例說明。

  • 當第一次執行main.js時,調用棧的初始初始狀態是這樣的(上也說了,script腳本算是一個宏任務):


js執行機制:event loop(多圖理解)


  • 當調用secondFunction時,此刻調用棧情況:


js執行機制:event loop(多圖理解)


  • 在secondFunction裡還調用了firstFunction,此刻調用棧:


js執行機制:event loop(多圖理解)


  • 當執行完了firstFunction:


js執行機制:event loop(多圖理解)


  • 同樣,執行完了secondFunction之後,就算執行完了main.js這個宏任務了,所以會有以下變化:


js執行機制:event loop(多圖理解)




js執行機制:event loop(多圖理解)


以上是調用棧的執行原理。。

請結合原理和上面詳細版的事件循環好好理解當中的過程。

練習理解

以這份代碼為例子,按照上述描述的過程,判斷輸出順序

console.log('1');
setTimeout(function() {
console.log('2');
process.nextTick(function() {
console.log('3');
})
new Promise(function(resolve) {
console.log('4');
resolve();
}).then(function() {
console.log('5')
})
})
process.nextTick(function() {
console.log('6');
})
new Promise(function(resolve) {
console.log('7');
resolve();
}).then(function() {
console.log('8')
})
setTimeout(function() {
console.log('9');
process.nextTick(function() {
console.log('10');
})
new Promise(function(resolve) {
console.log('11');
resolve();
}).then(function() {
console.log('12')
})
})

分析思路:

  1. 執行同步任務,輸出了1
  2. 代碼走到第一個setTimeout,產生第一個宏任務
  3. 代碼走到process.nextTick,產生1個微任務
  4. 代碼走到new Promise,執行同步任務輸出了7,併產生了一個微任務(then方法)
  5. 代碼走到第二個setTimeout,產生第二個宏任務
  6. 代碼走完了,此刻是已經執行完了該script代碼,第一個宏任務完了。此刻有一個宏任務隊列,由兩個setTimeout任務構成,有一個微任務隊列,由process.nextTick和promise.then任務構成。
  7. 按照event loop流程,宏任務執行完就執行所有微任務,process.nextTick是較其他微任務優先的,先輸出6,接著輸出8
  8. 執行新的宏任務,在宏任務隊列中執行最開始入隊的宏任務,即第一個setTimeout,執行裡面的同步任務,輸出2和4,併產生了兩個微任務
  9. 新的宏任務執行完了(第一個setTimeout),執行此刻的所有微任務,輸出3、5
  10. 開始新的宏任務(第二個setTimeout),執行裡面的同步腳本,輸出9、11。同樣的,產生了兩個微任務
  11. 執行微任務,輸出10、12

所以最終的輸出順序是1-7-6-8-2-4-3-5-9-11-10-12

最後

我這裡沒有過多的用文字述說很多,因為其實那個詳細版的那個流程圖就已經把最關鍵的說出來了,大家好好體會那個圖,結合最後的例子。

這裡說的主要是js客戶端的event loop ,關於node.js服務器端的event loop是不同的,這裡就不在這裡敘述說了。

文章始發與 K前端 微信公眾號

"

相關推薦

推薦中...