JavaScript 微任務



JavaScript 中的微任務是小的函式,它們在建立它們的函式或程式程式碼完成後以及 JavaScript 執行棧為空時執行。微任務在任何宏任務(例如 setImmediate() 和 setTimeout())之前執行。微任務用於實現諸如 Promise 之類的特性。

JavaScript 是一種單執行緒程式語言。但是,您可以使用 Promise、回撥和非同步函式來並行執行 JavaScript 程式碼。

JavaScript 基於事件迴圈執行程式碼。事件迴圈負責執行程式碼、處理程式碼、收集事件資料以及執行子任務。

讓我們首先了解 JavaScript 事件迴圈。

JavaScript 事件迴圈

事件迴圈逐行執行 JavaScript 程式碼。它將程式碼新增到呼叫棧中,這是一個用於執行它的佇列。

JavaScript 包含兩種型別的佇列來執行任務。

  • 微任務佇列
  • 宏任務佇列

當呼叫棧佇列為空時,事件迴圈執行微任務佇列中的所有任務。之後,它執行宏任務佇列中的所有函式和程式碼。

JavaScript Event Loop

在瞭解微任務和宏任務之後,我們將進一步瞭解 JavaScript 程式碼的執行。

什麼是 JavaScript 中的微任務?

在 JavaScript 中,微任務是由 Promise 或非同步函式生成的較短函式,稍後會被使用。

以下是微任務的列表。

  • Promise 回撥
  • 佇列微任務

無論您在使用 Promise 程式碼時將哪個回撥函式作為 then()、catch() 或 finally() 方法的引數傳遞,它都會新增到微任務佇列中。

首先,JavaScript 執行引擎執行整個指令碼,將來自主執行緒的程式碼新增到呼叫棧中,並將微任務新增到微任務佇列中。當完成執行呼叫棧中的所有任務後,它完成執行微任務佇列中的所有任務。

讓我們透過下面的示例來了解它。

示例

在下面的程式碼中,我們在指令碼開始時列印開始訊息,在指令碼結束時列印結束訊息。

在中間,我們定義了 Promise,它會立即被解析。之後,我們使用 then() 方法使用 Promise,並列印 Promise 返回的訊息。

<html>
<body>
   <div id = "output"> </div>
   <script>
      const output = document.getElementById("output");
      output.innerHTML += "The start of the code execution. <br>";
      
      // Creating the promise
      let promise = new Promise(function (resolve, reject) {
         resolve("The promise is resolved. <br>");
      });
      
      // Consuming the promise code
      promise.then(function (result) {
         output.innerHTML += result;
      });
      output.innerHTML += "The end of the code execution. <br>";
   </script>
</body>
</html>

輸出

The start of the code execution.
The end of the code execution.
The promise is resolved.

上面程式碼的輸出中發生了一些有趣的事情。

在輸出中,您可以看到它最後列印了開始、結束和 Promise 訊息。

現在,問題是為什麼會出現這種情況。答案是 then() 方法的回撥函式被新增到微任務佇列中,並且只有在呼叫棧為空時才會執行。

什麼是宏任務?

現在,讓我們瞭解什麼是宏任務。

宏任務也是一個短函式,它在呼叫棧和微任務佇列中的所有程式碼執行之後執行。

JavaScript 執行時引擎將宏任務新增到宏任務佇列中。

以下方法生成的回撥函式將新增到宏任務佇列中。

  • setTimeout
  • setInterval
  • setImmediate

讓我們透過下面的示例來了解宏任務。

示例

在下面的程式碼中,我們添加了開始訊息、setTimeOut() 方法和結束訊息。

在 setTimeOut() 方法中,我們將回調函式作為第一個引數傳遞,在輸出中列印訊息,並設定 0 秒延遲。

<html>
<body>
   <div id = "demo"> </div>
   <script>
      let output = document.getElementById("demo");
      output.innerHTML += "The start of the code execution.<br>";
      setTimeout(function () {
         output.innerHTML += "The code execution is being delayed for 0 seconds. <br>";
      }, 0);
      output.innerHTML += "The end of the code execution.<br>";
   </script>
</body>
</html>

輸出

The start of the code execution.
The end of the code execution.
The code execution is being delayed for 0 seconds.

上面程式碼的輸出也很有趣。

它首先列印開始訊息,然後列印結束訊息,最後列印來自 setTimeOut() 方法的訊息。

在這裡,我們為 setTimeOut() 方法設定了 0 延遲。儘管如此,它仍然在最後執行,因為 JavaScript 執行引擎將回調函式新增到宏任務佇列中。

讓我們透過下面的示例一起了解微任務和宏任務。

示例

在下面的程式碼中,我們添加了帶有 0 延遲的 setTimeOut() 方法,並且回撥函式列印訊息。

之後,我們使用 Promise() 建構函式定義了一個 Promise,並使用 then() 方法使用了 Promise 程式碼。

最後,我們列印了結束方法。

<html>
<body>
   <div id = "output"> </div>
   <script>
      let output = document.getElementById("output");
      output.innerHTML += "Start <br>";
      setTimeout(function () { // Macro task
         output.innerHTML += "In setTimeOut() method. <br>";
      }, 0);

      let promise = new Promise(function (resolve, reject) {
         resolve("In Promise constructor. <br>");
      });
      promise.then(function (value) { // Micro tasks
         output.innerHTML += value;
      });
      output.innerHTML += "End <br>";
   </script>
</body>
</html>

輸出

Start
End
In Promise constructor.
In setTimeOut() method.

讓我們瞭解上面示例的輸出。

首先,由於 JavaScript 呼叫棧,它列印“開始”訊息。

之後,它將 setTimeOut() 方法的回撥函式新增到宏任務佇列中。

接下來,它將 then() 方法的回撥函式新增到微任務佇列中。

接下來,它執行程式碼的最後一行並列印“結束”訊息。

現在,呼叫棧為空。因此,它執行微任務佇列中的所有任務。所以,它完成了then()方法回撥函式的執行。

現在,呼叫棧和微任務佇列都為空。因此,它執行宏任務佇列中的所有任務,並完成setTimeOut()方法回撥函式的執行。

本章演示了JavaScript執行引擎如何執行程式碼。如果要更改程式碼的執行順序,需要注意微任務和宏任務的使用。

廣告