为什么JavaScript中有微任务和(宏)任务之分?

8

从概念上来说,只有一个队列来处理任务对于大多数用例似乎已经足够了。那么为什么要使用多个队列,并将其区分为“微任务”和“(宏)任务”呢?


1
@VLAZ 但是目前的设计显然过于复杂。选择这种设计肯定有优势,而不是选择一个只有单个队列(或者在同一级别上有多个队列而没有嵌套的队列)的简单设计。 - Bergi
6
不确定这是否真的是一个意见问题...需要微任务的一个明显原因是任务优先级。我们希望一些任务在整个JS作业结束后被延迟,但在下一个“任务”发生之前执行。例如(并且微任务队列是在HTML中首次实现的地方):MutationObserver回调,所有在同一JS作业中发生的变异都需要合并为一个单独的事件,在下一个任务发生之前触发,以便我们避免成为一个呈现帧延迟。 - Kaiido
2
@Kaiido,您能否将其扩展为一个答案?特别是历史角度会很有趣。 - Bergi
3
在查看 W3C 邮件列表后(请注意,我在2013年并未参与规范问题),我发现创建微任务队列的另一个主要动机是废弃的 Object.observe,类似于 MutationObserver,它将各种事件合并为单个回调。他们还预见到即将推出的 ES Promises、ES WeakRefs(不确定为什么)和 HTML Custom Elements 回调,这些都需要微任务队列。同时请注意,还有多个(宏)任务来源。 - Kaiido
1个回答

7
拥有多个(宏)任务队列可以实现任务优先级。例如,用户代理(UA)可以选择将用户事件(例如单击事件)优先于网络事件,即使后者实际上是由系统首先注册的,因为前者可能对用户更加可见并需要更低的延迟。在HTML中,这是通过Event Loop's Processing model的第一步允许的,该步骤规定UA必须从其任务队列之一中选择要执行的下一个任务。(注意:HTML规范不要求存在多个队列,但它们定义了多个task sources以保证类似任务的执行顺序)。那么,为什么还需要另一个称为microtask queue的东西呢?我们可以找到一些早期关于“如何”实现它的讨论,但我没有深入挖掘谁最初提出了这个想法以及用例是什么。
然而,从讨论 我发现的 中可以看出,有几个需要这种机制的提案正在进行中:
  • 变异观察器
  • 现已弃用的ES Object.observe()
  • 当时即将推出的 ES Promises
  • 同样是当时即将推出的,但我不知道为什么要引用它 ES WeakRefs
  • HTML自定义元素回调

前两个也是浏览器中首先实现的,我们可以大概说,它们的使用情况是实现这种新类型队列的主要原因。

两者实际上做了类似的事情:它们监听对象(或DOM树)上的更改,并将在作业期间发生的所有更改合并为单个事件(不要读作Event)
有人可能会认为,此事件可以排队到下一个(宏)任务中,具有最高优先级,但事件循环已经有点复杂了,并且并非每个作业都必须来自任务
例如,渲染实际上是每个事件循环迭代的一部分,只是大多数时间它会提前退出,因为现在不是渲染的时间。
因此,如果您在渲染帧期间进行DOM修改,则可以使修改被渲染,在整个渲染完成后才获得回调。
由于观察者的主要用例是在观察到的更改触发性能重负副作用之前对其进行操作,因此我想您可以看出,在执行修改的作业之后立即插入回调的方法是必要的。

PS:那时我对事件循环并不了解,我离规范还很远,可能在这个答案中加入了一些年代错误的东西。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接