js事件循环:微任务和宏任务

浏览器JavaScript执行流程以及Node.js中的流程均基于事件循环

了解事件循环的工作方式对于优化(有时对于正确的体系结构)非常重要。

在本章中,我们首先介绍有关事物如何工作的理论细节,然后介绍该知识的实际应用。

事件循环

事件循环的概念是很简单的。有一个无限循环,JavaScript引擎等待任务,执行任务,然后休眠,等待更多任务。

引擎的一般算法:

  1. 当有任务:
    • 从最早的任务开始执行它们。
  2. 休眠直到出现任务,然后转到步骤1。

这是浏览页面时看到的形式化信息。JavaScript引擎大部分时间不执行任何操作,仅在脚本/处理程序/事件被激活时才运行。

任务示例:

  • …但是我们也可能希望在任务执行过程中显示一些东西,例如进度条。

    如果我们使用来将繁重的任务分成几部分setTimeout,则更改将在它们之间绘制出来。

    这看起来更漂亮:

    现在,

    显示的是的增加值i,这是一种进度条。

    用例3:在事件发生后采取措施

    在事件处理程序中,我们可能会决定推迟一些操作,直到事件冒泡并在所有级别上得到处理。我们可以通过将代码包装在零延迟setTimeout中来做到这一点。

    在分派自定义事件一章中,我们看到了一个示例:自定义事件menu-open是在setTimeout中分派的,因此它在完全处理“ click”事件之后发生。

    menu.onclick = function() {
      // ...
    
      // create a custom event with the clicked menu item data
      let customEvent = new CustomEvent("menu-open", {
        bubbles: true
      });
    
      // dispatch the custom event asynchronously
      setTimeout(() => menu.dispatchEvent(customEvent));
    };
    

    宏任务和微任务

    微任务仅来自我们的代码。它们通常是由Promise创建的:处理程序.then/catch/finally的执行成为微任务。微任务也被“秘密使用” await,因为它是Promise处理的另一种形式。

    还有一个特殊功能queueMicrotask(func)func可在微任务队列中排队等待执行。

    在每个宏任务执行之后,引擎会立即运行任务队列中的所有任务,然后再运行其他宏任务或渲染或其他任何操作。**

    例如,看一下:

    setTimeout(() => alert("timeout"));
    
    Promise.resolve()
      .then(() => alert("promise"));
    
    alert("code");
    

    这将是什么顺序?

    1. code 首先显示,因为它是常规的同步调用。
    2. promise显示第二个,因为它.then通过微任务队列,并在当前代码之后运行。
    3. timeout 最后显示,因为它是一个宏任务。

    更丰富的事件循环图片如下所示(顺序是从上到下,即:首先是脚本,然后是微任务,渲染等):


    在执行任何其他事件处理或呈现或执行任何其他宏任务之前,所有微任务都已完成。

    这很重要,因为它可以确保微任务之间的应用程序环境基本相同(没有鼠标坐标更改,没有新的网络数据等)。

    如果我们想异步执行一个函数(在当前代码之后),但是在呈现更改或处理新事件之前,可以使用进行调度queueMicrotask

    这是一个带有“计数进度条”的示例,与之前显示的示例类似,但queueMicrotask用于代替setTimeout。您可以看到它在最后渲染。就像同步代码一样:

    概括

    更详细的事件循环算法(尽管与规范相比仍简化了):

    1. 宏任务队列中出队并运行最早的任务(例如“脚本”)。
    2. 执行所有微任务
      • 当微任务队列不为空时:
        • 出队并运行最早的微任务。
    3. 渲染更改(如果有)。
    4. 如果宏任务队列为空,请等待直到出现宏任务。
    5. 转到步骤1。

    要安排新的宏任务

    • 使用零延迟setTimeout(f)

    这可以用于将繁重的计算任务分解为多个部分,以使浏览器能够对用户事件做出反应并显示它们之间的进度。

    另外,在事件处理程序中用于安排事件完全处理(冒泡完成)后的操作。

    安排新的微任务

    • 使用queueMicrotask(f)
    • 诺言处理程序还会通过微任务队列。

    微任务之间没有UI或网络事件处理:它们立即一个接一个地运行。

    因此,您可能想queueMicrotask异步执行功能,但要在环境状态下执行。

    Web Worker
    对于不应该阻塞事件循环的长时间繁琐的计算,我们可以使用Web Workers。
    这是在另一个并行线程中运行代码的方式。
    Web Workers可以与主进程交换消息,但是它们具有自己的变量和事件循环。
    Web Worker没有访问DOM的权限,因此它们对于同时使用多个CPU内核的计算非常有用。

    参考

    Event loop: microtasks and macrotasks

你可能感兴趣的:(js事件循环:微任务和宏任务)