Why does JavaScript have microtasks?

background

I will study this problem from a problem in the group. The following is the original problem:

Why have microtasks instead of just macrotasks? Suppose you were asked to design the event loop, how would you design it? Does it have to be this way, and what's the problem if it doesn't? If you leave the browser, are there other scenarios?

A total of 4 questions, let's go step by step.

Why have micro-tasks, and just use macro-tasks?

To answer this question, we must first understand the evolution of the Web.

Early JavaScript

At the beginning, the browser is JS responsible for processing user interaction and logic, and starts rendering after processing. The execution logic at this time is as follows:

网页加载,执行 JS 脚本->浏览器渲染->等待用户交互->处理交互相关的 JS 脚本->渲染...
复制代码

However, this will cause the JS execution time to be too long, the page to be unresponsive for a long time, and the user experience will be poor .

the emergence of tasks

In order to solve this problem, an asynchronous mechanism is provided, that is, using setTimeoutand setintervalcreating asynchronous tasks. This time it becomes this:

网页加载,执行 JS 脚本(无异步任务)->浏览器渲染->等待用户交互->处理交互相关的 JS 脚本->渲染...


网页加载,执行 JS 脚本(有异步任务)->浏览器渲染->处理异步任务->等待用户交互->处理交互相关的 JS 脚本->渲染...
复制代码

Whether it is loading a web page for the first time, or a JS script executed interactively by a user, or an asynchronous task created by setTimeoutwaiting , it is a task, that is, a "macro task". So in summary:

宏任务->渲染->宏任务->渲染...
复制代码

The above reference is from MDN:

Since (setTimeout() and setInterval()) were added to Web API, the JavaScript environment provided by the browser has gradually begun to include powerful features such as task scheduling and multi-threaded application development. Understanding how the JavaScript runtime schedules and runs code can be very useful for understanding queueMicrotask(). -MDN

Each agent is driven by an event loop, which is responsible for collecting user events (including user events as well as other non-user events, etc.) and queuing tasks to execute callbacks when appropriate. It then executes all pending JavaScript tasks (macro tasks), then microtasks, and then performs some necessary rendering and drawing operations before starting the next loop. -MDN

Why have microtasks

微任务的出现,使得在宏任务执行完,到浏览器渲染之前,可以在这个阶段插入任务的能力

上几个引用供参考:

一个 微任务(microtask)就是一个简短的函数,当创建该函数的函数执行之后,并且 只有当 Javascript 调用栈为空,而控制权尚未返还给被 user agent 用来驱动脚本执行环境的事件循环之前,该微任务才会被执行。 事件循环既可能是浏览器的主事件循环也可能是被一个 web worker 所驱动的事件循环。这使得给定的函数在没有其他脚本执行干扰的情况下运行,也保证了微任务能在用户代理有机会对该微任务带来的行为做出反应之前运行。 ——MDN

通过使用异步 JavaScript技术(例如承诺)允许主代码在等待请求结果的同时继续运行,进一步缓解了这种情况。但是,在更基础级别上运行的代码(例如包含库或框架的代码)可能需要一种方法来安排代码在安全时间运行,同时仍在主线程上执行,而与任何单个请求的结果或任务。——MDN

以及「What was the motivation for introducing a separate microtask queue which the event loop prioritises over the task queue?」这个问题下的回答。

只用宏任务不行吗?

先看下面一段代码:

<html>
<head>
  <style>
    div {
      padding: 0;
      margin: 0;
      display: inline-block;
      widtH: 100px;
      height: 100px;
      background: blue;
    }
    #microTaskDom {
      margin-left: 10px;
    }
  </style>
</head>
<body>
  <div id="taskDom"></div>
  <div id="microTaskDom"></div>
  <script>
    window.onload = () => {
        setTimeout(() => {
          taskDom.style.background = 'red'
          setTimeout(() => {
            taskDom.style.background = 'black'
          }, 0);
          
          microTaskDom.style.background = 'red'
          Promise.resolve().then(() => {
            microTaskDom.style.background = 'black'
          })
        }, 1000);
    }
  </script>
</body>

</html>
复制代码

如果使用 setTimeout 立马修改背景色,会闪现一次红色背景,而使用 Promise 则不会。

因为微任务会在渲染之前完成对背景色的修改,等到渲染时就只需要渲染黑色。

而使用任务,第一次渲染会渲染红色,等到下一次任务时修改为黑色,之后的渲染阶段才会再次渲染为黑色。

大家可以拷贝上面的代码自行尝试一下,看下效果。

h19y5-cadju.gif

假设让你设计事件循环,你会如何设计?

目前这种设计基本可以了,在渲染前以及渲染后都已经可以完成一系列操作。

是不是必须这样,不这样会有什么问题?

不是,虽然我没遇到过需要利用这个机制处理的场景,但是基本上都能有解决办法。

宏任务以及微任务的出现,都是从用户体验以及性能方面进行考虑的,它们的出现可以让用户得到更好的使用体验。

假如脱离浏览器,有没有其它场景?

宏任务和微任务的出现,实际上是让 JS 脚本有了在渲染阶段前后可以完成一些操作的能力,类似于生命周期的概念。

所以像Vue、React的生命周期,Node.js 的事件循环都是一种场景。

以下是 JS 版本的一段伪码:

总结

技术的发展是不断精进的,JS 也不是一下子就到了现在这么完善的地步,都是一步一步根据问题来的。

而宏任务和微任务,本质上也是让 JS 脚本能够在渲染阶段前后,有了完成某些功能的能力。


不是越多才越好,我是 Vip,关注我,用极简的思路学前端,轻松应对前端工作

Guess you like

Origin juejin.im/post/7086307441847042062