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运行引擎如何执行代码。如果要更改代码的执行顺序,需要注意微任务和宏任务的使用。

广告