浏览器中的事件循环是如何同时处理事件队列、作业队列、渲染队列的呢?

Bar*_*inM 5 javascript asynchronous callback event-loop promise

上周我开始用nodejs学习Javascript后端。在使用async函数时,我想从各个方面了解这一点,并开始对此主题进行研究。
在 jsconf 中找到了 Jake Archibald 的演示,并尝试了解什么是事件循环以及事件循环如何在不同队列中运行以及它可以处理哪些队列。

我认为本演示文稿中的一张图表非常适合理解所有内容。

链接视频的屏幕截图

但我想知道事件循环如何处理由承诺回调组成的作业队列。
我们可以在该图中为该队列添加另一条路线吗?

该图显示了 3 个标记为“事件队列”、“渲染队列”和“微任务队列”的循环,以及第四个未标记的循环。(我尝试将其实现到图中:))

我想知道的另一个问题是,事件循环对渲染队列做了什么?事件循环是否将其发送到另一个地方?因为我们知道事件循环会将事件队列或作业队列中的函数发送到 javascript 引擎中的调用堆栈来运行这些函数。
但渲染队列有请求动画帧和其他样式元素。
事件循环是否将请求动画帧部分发送到 javascript 引擎并将其他布局、样式和绘制部分发送到布局引擎?

Kai*_*ido 5

该图过度简化了事物的工作原理,为了详细了解发生的情况,我邀请您直接查看规范,这些天实际上已经变得非常可读且易于导航。

从那里你会看到

[t]微任务队列不是任务队列。

如果我们自己采取过度简化的方式,只用我们感兴趣的内容来解释事件循环例程,

  • 来自其他进程和先前任务的消息将在各种任务源中对新任务进行排队,它们本身以更少的任务队列结束。(这为杰克图表的左侧循环提供了动力)。
  • 在每次迭代中,事件循环的第一步是从这些任务队列之一中选择第一个任务(根据需要选择,这允许任务优先级排序)。
  • 主要任务完成后(规范中的步骤 7),事件循环将在所谓的microtask-checkpoint中查看微任务队列。
  • 仅对于自上次迭代以来活动显示监视器确实发出同步脉冲的文档(在 60Hz 监视器上每 16.67 毫秒一次),它会更新渲染(Jake 图中的右循环,规范中的步骤 11.6~11.15)。
  • 在这些步骤中,它将执行一些任务,例如触发 UI 事件、更新动画和运行动画帧回调。每次这些算法之一调用回调时,用户代理都必须根据 运行算法后的清理执行新的微任务检查点,因此例如每个动画帧回调都会与这样的检查点交错,其中一些算法甚至直接执行微任务检查点。

因此,这意味着微任务检查点不仅仅在事件循环中的单个点中执行,它在主任务之后执行一次,并且在更新渲染步骤中的每个回调执行之后执行多次。

用户代理不能只是延迟微任务,它必须在排队的任务完成后立即执行它,即,从事件循环的角度来看,微任务是同步执行的。

同样,“渲染队列”也不是任务队列,它必须在浏览器有“渲染机会”时运行,它不能成为任务优先级机制的一部分(更多内容请参见此处)。


因为我们知道事件循环会将事件队列或作业队列中的函数发送到 javascript 引擎中的调用堆栈来运行这些函数。

不完全是。首先请记住,javascript 执行只是浏览器功能的一部分,许多任务根本不涉及 javascript,例如,即使您从网络浏览器中禁用了 javascript,事件循环仍然需要运行,它仍然需要更新渲染,仍然需要发送表单,仍然需要对网络事件(例如媒体加载等)作出反应。
事件循环所做的是运行任务的步骤,但这些步骤可能有很多不同东西,他们不一定会发送任何东西到任何地方。

另外,规范根本没有说明计算应该如何分布,但关于渲染,我们可以假设简而言之,从更新渲染到第 14 步的所有操作都应该在同一进程上完成(必须按顺序运行) ),但是第 15 步(“更新渲染”)实际上会将所有内容绘制到显示器上,可以(我认为在所有现代浏览器中)传递到另一个进程,专门用于此渲染任务。
您可以查看Chromium 的文档,了解它们如何管理浏览器进程和渲染器之间的内部通信。