事件循环(Event loops)

基本概念

每个代理都是由事件循环驱动的,事件循环负责收集用事件(包含用户事件以及其余非用户事件等)、对工作进行排队以便在适合的时候执行回调。而后它执行所有处于期待中的 JavaScript 工作(宏工作),而后是微工作,而后在开始下一次循环之前执行一些必要的渲染和绘制操作。

事件循环的职责

  • 收集事件
  • 保护工作队列
  • 保护微工作队列
  • 调用渲染引擎

一次残缺的事件循环(运行环境Chrome 87)

  • 事件循环开始
  • 执行工作队列中的第一个工作
  • 顺次执行微工作队列中所有的微工作
  • 第一个工作退出工作队列
  • 必要的渲染和绘制操作
  • 反复以上步骤直到工作队列清空
  • 事件循环完结

这里的工作队列指的是事件循环开始迭代时的工作队列

在事件循环开始迭代之后退出到队列中的工作须要在下一次迭代开始之后才会被执行。

宏工作(Macrotasks)和微工作(Microtasks)

Macrotasks通常指的是Tasks(下文有介绍),Macrotasks在规范上是查不到任何相干形容的,这个概念从何而来不得而知,然而业界广泛抵赖这个概念以跟Microtasks做辨别。

Microtasks是写入HTML Standard的规范,并且在浏览器环境有相干api queueMicrotask。

实际上浏览器中只有工作(Tasks)微工作(Microtasks)之分,上面来讲讲他们的概念和区别。

工作(Tasks)和微工作(Microtasks)的概念和关系

工作(Tasks)
基本概念
一个 工作 就是由执行诸如从头执行一段程序、执行一个事件回调或一个 interval/timeout 被触发之类的规范机制而被调度的任意 JavaScript 代码。这些都在 工作队列(task queue)上被调度。
工作的增加

在以下机会,工作会被增加到工作队列:

  • 一段新程序或子程序被间接执行时(比方从一个控制台,或在一个 <script> 元素中运行代码)。
  • 触发了一个事件,将其回调函数增加到工作队列时。
  • 执行到一个由 setTimeout()setInterval() 创立的 timeout 或 interval,以至相应的回调函数被增加到工作队列时。
setTimeout和setInterval

当调用setTimeout(fn, time)setInterval(fn, time)增加工作时,第二个参数time指的是工作被增加到工作队列的工夫,而不是fn被执行的工夫,fn被执行的工夫应该是被增加的工作执行的工夫。

微工作(Microtasks)
基本概念
一个 微工作(microtask)就是一个简短的函数,当创立该函数的函数执行之后,并且只有当 Javascript 调用栈为空,而控制权尚未返还给被 user agent 用来驱动脚本执行环境的事件循环之前,该微工作才会被执行。 事件循环既可能是浏览器的主事件循环也可能是被一个 web worker 所驱动的事件循环。这使得给定的函数在没有其余脚本执行烦扰的状况下运行,也保障了微工作能在用户代理有机会对该微工作带来的行为做出反馈之前运行。
微工作的增加
JavaScript 中的 promises 和 Mutation Observer API 都应用微工作队列去运行它们的回调函数,但当可能推延工作直到以后事件循环过程完结时,也是能够执行微工作的机会。为了容许第三方库、框架、polyfills 能应用微工作,Window 裸露了 queueMicrotask() 办法,而 Worker 接口则通过WindowOrWorkerGlobalScope mixin 提供了同名的 queueMicrotask() 办法。
微工作存在的意义

当你想在一次工作完结之前执行一些代码时(而不是下一次工作再执行),你能够应用微工作,具体的应用场景能够看看何时应用微工作。

工作和微工作之间的区别
  • 当执行来自工作队列中的工作时,在每一次新的事件循环开始迭代的时候运行时都会执行队列中的每个工作。在每次迭代开始之后退出到队列中的工作须要在下一次迭代开始之后才会被执行
  • 每次当一个工作执行上下文为空的时候,微工作队列中的每一个微工作会顺次被执行。不同的是它会等到微工作队列为空才会进行执行——即便中途有微工作退出。换句话说,微工作能够增加新的微工作到队列中,并在当前任务完结之前且下一个工作开始执行之前执行完所有的微工作。
工作和微工作之间的关系

在写这篇文章的时候我有一个疑难,既然在一个工作执行上下文为空的时候,再执行微工作队列中的微工作。那微工作是在以后被执行工作来到工作队列之前执行还是之后执行?这就关系到一个工作的执行工夫应不应该包含微工作的执行工夫,以及事件循环的执行程序。

我查了一些材料,在HTML Standard和ECMA规范里如同并没有明确定义工作和微工作队列应该是蕴含关系还是互相独立的关系,所以工作和微工作之间的关系应该是浏览器厂商本人去实现的

我这里只测试了Chrome浏览器(版本号是87.0.4280.66)

能够看到Chrome是把Run Microtasks的执行工夫算在了Task的执行工夫里的,所以在Chrome环境里工作和微工作是蕴含关系,也就是说当一个微工作执行时,必然有一个工作在同时执行。


参考文献

  • HTML Standard
  • ECMA-262
  • QueueMicrotask
  • Microtask Guide In Depth