为了协调事件、用户交互、脚本、渲染、网络以及其他方面,用户代理必须使用本节所述的事件循环。每个 代理 都有一个关联的事件循环,该循环对该代理是唯一的。
同源窗口代理 的 事件循环 被称为窗口事件循环。专用工作者代理、共享工作者代理 或 服务工作者代理 的 事件循环 被称为工作者事件循环。Worklet 代理 的 事件循环 被称为 Worklet 事件循环。
事件循环 不一定对应于实现线程。例如,多个 窗口事件循环 可以在单个线程中协作调度。
但是,对于 [[CanBlock]] 设置为 true 的各种工作者 代理,JavaScript 规范确实对它们提出了关于 前进进度 的要求,这实际上相当于在这些情况下需要专用的代理线程。
事件循环 有一个或多个任务队列。任务队列 是一个 任务 的 集合。
任务队列 是 集合,而不是 队列,因为 事件循环处理模型 从所选队列中获取第一个 可运行 的 任务,而不是将第一个任务 出队。
任务封装了负责以下工作的算法:
-
事件
在特定
EventTarget
对象上调度Event
对象通常由专用任务完成。并非所有事件都使用 任务队列 进行调度;许多事件是在其他任务期间调度的。
-
解析
HTML 解析器 对一个或多个字节进行标记化,然后处理任何生成的标记,通常是一个任务。
-
回调
调用回调通常由专用任务完成。
-
使用资源
当算法 获取 资源时,如果获取以非阻塞方式发生,则在部分或全部资源可用后对资源的处理由任务执行。
-
对 DOM 操作做出反应
某些元素具有响应 DOM 操作而触发的任务,例如当该元素被 插入文档 中时。
形式上,任务是一个 结构,它具有:
-
步骤
一系列步骤,指定任务要完成的工作。
-
来源
任务来源 之一,用于对相关任务进行分组和序列化。
-
文档
与任务关联的
Document
,或者对于不在 窗口事件循环 中的任务,则为 null。 -
脚本评估环境设置对象集
如果 任务 的 文档 为 null 或 完全活动,则该任务是可运行的。
根据其 来源 字段,每个 任务 都被定义为来自特定的任务来源。对于每个 事件循环,每个 任务来源 必须与特定的 任务队列 相关联。
本质上,标准中使用 任务来源 来分离逻辑上不同类型的任务,用户代理可能希望区分这些任务。用户代理使用 任务队列 来合并给定 事件循环 中的任务来源。
例如,用户代理可以为鼠标和键盘事件(与之关联的是 用户交互任务来源)使用一个 任务队列,而所有其他 任务来源 都关联到另一个任务队列。然后,利用 事件循环处理模型 第一步中赋予的自由,它可以在四分之三的时间内优先处理键盘和鼠标事件,而不是其他任务队列,从而保持界面的响应速度,但不会使其他任务队列饿死。请注意,在此设置中,处理模型仍然强制用户代理永远不会无序地处理来自任何一个 任务来源 的事件。
每个 事件循环 都有一个当前正在运行的任务,它是一个 任务 或 null。最初,它是 null。它用于处理重入。
每个 事件循环 都有一个微任务队列,它是 微任务 的 队列,最初为空。微任务是一种口语化的说法,指的是通过 排队微任务 算法创建的 任务。
每个 事件循环 都有一个执行微任务检查点的布尔值,最初为 false。它用于防止 执行微任务检查点 算法的重入调用。
每个 窗口事件循环 都有一个 DOMHighResTimeStamp
类型的最后渲染机会时间,最初设置为零。
每个 窗口事件循环 都有一个 DOMHighResTimeStamp
类型的最后空闲时间段开始时间,最初设置为零。
要获取 窗口事件循环 loop
的同循环窗口,请返回所有其 相关代理 的 事件循环 为 loop
的 Window
对象。
要在 任务来源 source
上排队一个任务,该任务执行一系列步骤 steps
,可以选择提供一个事件循环 event loop
和一个文档 document
:
-
如果未提供
event loop
,则将event loop
设置为 隐含事件循环。 -
如果未提供
document
,则将document
设置为 隐含文档。 -
创建一个新的 任务
task
。 -
将
task
的 步骤 设置为steps
。 -
将
task
的 来源 设置为source
。 -
将
task
的 文档 设置为document
。 -
将
task
的 脚本评估环境设置对象集 设置为空 集合。 -
令
queue
为event loop
上source
关联到的 任务队列。 -
将
task
追加 到queue
。
未能将事件循环和文档传递给 任务排队 意味着依赖于不明确且规范不佳的 隐含事件循环 和 隐含文档 概念。规范作者应该始终传递这些值,或者使用包装器算法 排队全局任务 或 排队元素任务 来代替。建议使用包装器算法。
要在 任务来源 source
上排队一个全局任务,该任务具有 全局对象 global
和一系列步骤 steps
:
-
如果
global
是Window
对象,则令document
为global
的 关联Document
;否则为 null。 -
排队一个任务,给定
source
、event loop
、document
和steps
。
要在 任务来源 source
上排队一个元素任务,该任务具有元素 element
和一系列步骤 steps
:
要排队一个执行一系列步骤 steps
的微任务,可以选择提供一个文档 document
:
-
如果未提供
document
,则将document
设置为 隐含文档。 -
创建一个新的 任务
microtask
。 -
将
microtask
的 步骤 设置为steps
。 -
将
microtask
的 来源 设置为微任务任务来源。 -
将
microtask
的 文档 设置为document
。 -
将
microtask
的 脚本评估环境设置对象集 设置为空 集合。
如果 微任务 在其初始执行期间 旋转事件循环,则可以将其移动到常规 任务队列。这是唯一一种会参考微任务的 来源、文档 和 脚本评估环境设置对象集 的情况;执行微任务检查点 算法会忽略它们。
任务排队时的隐含事件循环是可以从调用算法的上下文中推断出来的。这通常是明确的,因为大多数规范算法只涉及单个 代理(因此是单个 事件循环)。例外情况是涉及或指定跨代理通信的算法(例如,窗口和工作者之间);对于这些情况,不能依赖 隐含事件循环 概念,规范必须在 任务排队 时显式提供 事件循环。
在 事件循环 event loop
上排队任务时的隐含文档确定如下:
-
如果
event loop
不是 窗口事件循环,则返回 null。 -
如果任务是在元素的上下文中排队的,则返回元素的 节点文档。
-
如果任务是由或为 脚本 排队的,则返回脚本的 设置对象 的 全局对象 的 关联
Document
。 -
断言: 永远不会到达此步骤,因为前面的条件之一为真。真的吗?
隐含事件循环 和 隐含文档 的定义都很模糊,并且有很多超距作用。希望能够删除它们,特别是 隐含文档。请参阅 issue #4980。
事件循环 必须不断地运行以下步骤,直到它存在为止:
-
令
oldestTask
和taskStartTime
为 null。 -
如果 事件循环 具有至少一个 可运行 的 任务 的 任务队列,则:
-
令
taskQueue
为其中一个 任务队列,以 实现定义 的方式选择。请记住,微任务队列 不是 任务队列,因此在此步骤中不会选择它。但是,可能会在此步骤中选择与 微任务任务来源 关联的 任务队列。在这种情况下,下一步中选择的 任务 最初是一个 微任务,但它作为 旋转事件循环 的一部分被移动了。
-
将
taskStartTime
设置为 不安全的共享当前时间。 -
将
oldestTask
设置为taskQueue
中第一个 可运行 的 任务,并将其从taskQueue
中 移除。 -
如果
oldestTask
的 文档 不为 null,则 记录任务开始时间,给定taskStartTime
和oldestTask
的 文档。 -
执行
oldestTask
的 步骤。
-
-
令
taskEndTime
为 不安全的共享当前时间。[HRT] -
如果
oldestTask
不为 null,则: -
如果这是一个 窗口事件循环,并且在此 事件循环 的 任务队列 中没有 可运行 的 任务,则:
-
将此 事件循环 的 最后空闲时间段开始时间 设置为 不安全的共享当前时间。
-
令
computeDeadline
为以下步骤:-
令
deadline
为此 事件循环 的 最后空闲时间段开始时间 加上 50。未来 50 毫秒的上限是为了确保在人类感知的阈值内对新用户输入做出响应。
-
令
hasPendingRenders
为 false。 -
如果
hasPendingRenders
为 true,则: -
返回
deadline
。
-
-
对于此 事件循环 的 同循环窗口 中的每个
win
,对win
执行 开始空闲时间段算法,其中以下步骤:返回调用computeDeadline
的结果,粗略化 给定win
的 相关设置对象 的 跨源隔离功能。[REQUESTIDLECALLBACK]
-
-
如果这是一个 工作者事件循环,则:
窗口事件循环 eventLoop
还必须 并行 运行以下内容,只要它存在:
-
将
eventLoop
的 最后渲染机会时间 设置为 不安全的共享当前时间。 -
对于每个具有 渲染机会 的
navigable
,在 渲染任务来源 上 排队一个全局任务,给定navigable
的 活动窗口 以更新渲染:这可能会导致对 更新渲染 的冗余调用。但是,这些调用不会产生可观察到的效果,因为根据 不必要的渲染 步骤,将不需要渲染。实现可以引入进一步的优化,例如仅在尚未排队时才对该任务进行排队。但是,请注意,与任务关联的文档可能会在处理任务之前变为非活动状态。
-
令
frameTimestamp
为eventLoop
的 最后渲染机会时间。 -
令
docs
为所有 相关代理 的 事件循环 为eventLoop
的 完全活动 的Document
对象,任意排序,但必须满足以下条件:-
任何 容器文档 为
A
的Document
B
在列表中必须列在A
之后。 -
如果有两个文档
A
和B
都具有相同的非 null 容器文档C
,则A
和B
在列表中的顺序必须与其各自 可导航容器 在C
的 节点树 中的 包含阴影的树顺序 相匹配。
在迭代
docs
的以下步骤中,必须按照在列表中找到每个Document
的顺序对其进行处理。 -
-
过滤不可渲染的文档: 从
docs
中移除满足以下任一条件的任何Document
对象doc
:除了在 并行 步骤中进行检查外,我们还必须在此处检查渲染机会,因为共享同一个 事件循环 的某些文档可能不会同时具有 渲染机会。
-
不必要的渲染: 从
docs
中移除满足以下所有条件的任何Document
对象doc
: -
从
docs
中移除用户代理认为出于其他原因最好跳过更新渲染的所有Document
对象。标有 过滤不可渲染的文档 的步骤可防止用户代理在无法向用户呈现新内容时更新渲染。
标有 不必要的渲染 的步骤可防止用户代理在没有要绘制的新内容时更新渲染。
此步骤使用户代理能够出于其他原因阻止以下步骤运行,例如,为了确保某些 任务 在彼此之后立即执行,仅穿插 微任务检查点(而没有穿插 动画帧回调)。具体来说,用户代理可能希望将计时器回调合并在一起,而无需中间渲染更新。
-
对于
docs
中的每个doc
,显示doc
。 -
对于
docs
中的每个doc
,为doc
运行调整大小步骤。[CSSOMVIEW] -
对于
docs
中的每个doc
,为doc
运行滚动步骤。[CSSOMVIEW] -
对于
docs
中的每个doc
,为doc
评估媒体查询并报告更改。[CSSOMVIEW] -
对于
docs
中的每个doc
,为doc
更新动画并发送事件,将 相对高分辨率时间 给定frameTimestamp
和doc
的 相关全局对象 作为时间戳传入。[WEBANIMATIONS] -
对于
docs
中的每个doc
,为doc
运行全屏步骤。[FULLSCREEN] -
对于
docs
中的每个doc
,如果用户代理检测到与CanvasRenderingContext2D
或OffscreenCanvasRenderingContext2D
(context
)关联的后备存储已丢失,则它必须为每个此类context
运行上下文丢失步骤:-
令
canvas
为context
的canvas
属性的值(如果context
是CanvasRenderingContext2D
),否则为context
的 关联OffscreenCanvas
对象。 -
将
context
的 上下文丢失 设置为 true。 -
给定
context
,将渲染上下文重置为其默认状态。 -
令
shouldRestore
为在canvas
上 触发 名为contextlost
的事件的结果,该事件的cancelable
属性初始化为 true。 -
如果
shouldRestore
为 false,则中止这些步骤。 -
尝试通过使用
context
的属性创建后备存储并将其与context
关联来恢复context
。如果失败,则中止这些步骤。 -
将
context
的 上下文丢失 设置为 false。 -
在
canvas
上 触发 名为contextrestored
的事件。
-
-
对于
docs
中的每个doc
,为doc
运行动画帧回调,将 相对高分辨率时间 给定frameTimestamp
和doc
的 相关全局对象 作为时间戳传入。 -
令
unsafeStyleAndLayoutStartTime
为 不安全的共享当前时间。 -
对于
docs
中的每个doc
:-
令
resizeObserverDepth
为 0。 -
当 true 时:
-
重新计算样式并更新
doc
的布局。 -
令
hadInitialVisibleContentVisibilityDetermination
为 false。 -
对于使用 'auto' 作为 'content-visibility' 的值的每个元素
element
: -
如果
hadInitialVisibleContentVisibilityDetermination
为 true,则 继续。此步骤的目的是为了使初始视口距离确定(立即生效)反映在此循环的先前步骤中执行的样式和布局计算中。初始视口距离确定以外的其他距离确定在下一个 渲染机会 生效。[CSSCONTAIN]
-
如果
doc
具有活动大小调整观察:-
将
resizeObserverDepth
设置为给定doc
广播活动大小调整观察 的结果。 -
继续。
-
-
否则,中断。
-
-
如果
doc
已跳过大小调整观察,则给定doc
传递大小调整循环错误。
-
-
对于
docs
中的每个doc
,如果doc
的 焦点区域 不是 可聚焦区域,则为doc
的 视口 运行 聚焦步骤,并将doc
的 相关全局对象 的 导航 API 的 在正在进行的导航期间焦点已更改 设置为 false。例如,这可能是因为某个元素添加了
hidden
属性,导致它停止 被渲染。当input
元素被 禁用 时,也可能会发生这种情况。这将 通常 触发
blur
事件,并且可能触发change
事件。除了这种异步修复之外,如果 文档的焦点区域 被移除,则还有一个 同步修复。该修复 不会 触发
blur
或change
事件。 -
对于
docs
中的每个doc
,为doc
执行待处理的转换操作。[CSSVIEWTRANSITIONS] -
对于
docs
中的每个doc
,为doc
运行更新交叉观察步骤,将 相对高分辨率时间 给定now
和doc
的 相关全局对象 作为时间戳传入。[INTERSECTIONOBSERVER] -
对于
docs
中的每个doc
,给定unsafeStyleAndLayoutStartTime
为doc
记录渲染时间。 -
对于
docs
中的每个doc
,为doc
标记绘制时间。 -
对于
docs
中的每个doc
,更新doc
及其 节点可导航 的渲染或用户界面以反映当前状态。 -
对于
docs
中的每个doc
,给定doc
处理顶层移除。
-
如果用户代理当前能够向用户呈现 可导航 的内容,则该 可导航 就有渲染机会,同时考虑硬件刷新率限制和用户代理为性能原因进行的限制,但即使内容在视口之外也认为内容是可呈现的。
可导航 的 渲染机会 是根据硬件限制(例如显示刷新率)和其他因素(例如页面性能或其 活动文档 的 可见性状态 是否为 "visible
")确定的。渲染机会通常以固定的时间间隔出现。
本规范没有强制要求任何特定的模型来选择渲染机会。但例如,如果浏览器试图实现 60Hz 的刷新率,则渲染机会最多每秒出现 60 次(约 16.7 毫秒)。如果浏览器发现 可导航 无法维持此速率,则它可能会降低到该 可导航 每秒 30 次渲染机会,而不是偶尔丢帧。类似地,如果 可导航 不可見,则用户代理可以决定将该页面降低到每秒 4 次渲染机会,甚至更少。
当用户代理要执行微任务检查点时:
-
对于每个 责任事件循环 为此 事件循环 的 环境设置对象,在该 环境设置对象 上 通知已拒绝的 Promise。
-
执行 ClearKeptObjects().
当
WeakRef.prototype.deref()
返回一个对象时,该对象会一直保持活动状态,直到下次调用 ClearKeptObjects(),之后它将再次进行垃圾回收。
当 并行 运行的算法要等待稳定状态时,用户代理必须 排队一个微任务 来运行以下步骤,然后必须停止执行(算法的执行在微任务运行时恢复,如以下步骤所述):
-
运行算法的同步部分。
-
如算法步骤中所述,并行 恢复算法的执行(如果适用)。
同步部分 中的步骤用 标记。
算法步骤中说要旋转事件循环直到满足条件 goal
,这等效于替换以下算法步骤:
-
task
可以是 微任务。 -
令
task source
为task
的 来源。 -
令
old stack
为 JavaScript 执行上下文堆栈 的副本。 -
并行:
-
等待,直到满足条件
goal
。 -
在
task source
上 排队一个任务,以:-
将 JavaScript 执行上下文堆栈 替换为
old stack
。 -
执行原始算法中此 旋转事件循环 实例之后出现的任何步骤。
这将恢复
task
。
-
-
-
停止
task
,允许调用它的任何算法恢复。
与本规范和其他规范中的其他算法(其行为类似于编程语言函数调用)不同,旋转事件循环 更像是一个宏,它通过扩展成一系列步骤和操作来节省使用站点上的输入和缩进。
其步骤如下所示的算法:
-
做某事。
-
旋转事件循环,直到发生很棒的事情。
-
做其他事情。
是一个简写,在“宏扩展”之后变成
-
做某事。
-
令
old stack
为 JavaScript 执行上下文堆栈 的副本。 -
并行:
-
等待,直到发生很棒的事情。
-
在执行“做某事”的任务来源上 排队一个任务,以:
-
将 JavaScript 执行上下文堆栈 替换为
old stack
。 -
做其他事情。
-
-
下面是一个更完整的替换示例,其中事件循环是从并行工作中排队的任务内部旋转的。使用 旋转事件循环 的版本:
-
并行:
-
做并行的事情 1。
-
在 DOM 操作任务来源 上 排队一个任务,以:
-
做任务的事情 1。
-
旋转事件循环,直到发生很棒的事情。
-
做任务的事情 2。
-
-
做并行的事情 2。
-
完全扩展的版本:
-
并行:
-
做并行的事情 1。
-
令
old stack
为 null。 -
在 DOM 操作任务来源 上 排队一个任务,以:
-
做任务的事情 1。
-
将
old stack
设置为 JavaScript 执行上下文堆栈 的副本。
-
-
等待,直到发生很棒的事情。
-
在 DOM 操作任务来源 上 排队一个任务,以:
-
将 JavaScript 执行上下文堆栈 替换为
old stack
。 -
做任务的事情 2。
-
-
做并行的事情 2。
-
由于历史原因,本规范中的一些算法要求用户代理在运行 任务 时暂停,直到满足条件 goal
。这意味着运行以下步骤:
-
令
global
为 当前全局对象。 -
令
timeBeforePause
为给定global
的 当前高分辨率时间。 -
如有必要,更新任何
Document
或 可导航 的渲染或用户界面以反映当前状态。 -
等待,直到满足条件
goal
。当用户代理有一个已暂停的 任务 时,相应的 事件循环 不得运行更多 任务,并且当前正在运行的 任务 中的任何脚本都必须阻塞。但是,用户代理在暂停时应保持对用户输入的响应,尽管由于 事件循环 不执行任何操作,因此响应能力会降低。
暂停 对用户体验非常不利,尤其是在多个文档共享单个 事件循环 的情况下。鼓励用户代理尝试 暂停 的替代方法,例如 旋转事件循环,或者甚至根本不进行任何类型的暂停执行,只要在保持与现有内容兼容的同时这样做是可能的。如果发现影响较小的替代方案与 Web 兼容,本规范将很乐意进行更改。
在此期间,实现者应该意识到,用户代理可能尝试的各种替代方案可能会改变 事件循环 行为的细微方面,包括 任务 和 微任务 计时。即使这样做会导致它们违反 暂停 操作所暗示的精确语义,实现也应该继续进行试验。
本规范和其他规范中的许多功能都使用以下 任务来源。
-
DOM 操作任务来源
-
用户交互任务来源
此 任务来源 用于响应用户交互的功能,例如键盘或鼠标输入。
响应用户输入发送的事件(例如
click
事件)必须使用 使用用户交互任务来源排队 的 任务 来触发。[UIEVENTS] -
网络任务来源
此 任务来源 用于响应网络活动而触发的功能。
-
导航和遍历任务来源
-
渲染任务来源
编写与 事件循环 正确交互的规范可能很棘手。本规范使用独立于并发模型的术语,这加剧了这种情况,因此我们使用“事件循环”和“并行”之类的说法,而不是使用更熟悉的特定于模型的术语,例如“主线程”或“后台线程”。
默认情况下,规范文本通常在 事件循环 上运行。这源于正式的 事件循环处理模型,因为您可以最终将大多数算法追溯到在那里 排队 的 任务。
任何 JavaScript 方法的算法步骤都将由调用该方法的作者代码调用。作者代码只能通过排队的任务运行,这些任务通常来自 script
处理模型 中的某个位置。
从这个起点开始,首要的准则是,规范需要执行的任何可能会阻塞 事件循环 的工作都必须 并行 执行。这包括(但不限于):
-
执行大量计算;
-
显示面向用户的提示;
-
执行可能需要涉及外部系统(即“进程外”)的操作。
接下来的复杂之处在于,在 并行 的算法部分中,您不得创建或操作与特定 领域、全局 或 环境设置对象 关联的对象。(用更熟悉的术语来说,您不得直接从后台线程访问主线程的项目。)这样做会创建 JavaScript 代码可观察到的数据竞争,因为毕竟您的算法步骤是 并行 于 JavaScript 代码运行的。
但是,您可以操作来自 Infra 的规范级数据结构和值,因为它们与领域无关。它们永远不会在没有发生特定转换的情况下直接暴露给 JavaScript(通常 通过 Web IDL)。[INFRA] [WEBIDL]
为了影响可观察到的 JavaScript 对象的世界,您必须 排队一个全局任务 来执行任何此类操作。这可以确保您的步骤相对于 事件循环 上发生的其他事情正确交错。此外,您必须在 排队全局任务 时选择一个 任务来源;这决定了您的步骤相对于其他步骤的相对顺序。如果您不确定要使用哪个 任务来源,请选择一个听起来最适用的 通用任务来源。最后,您必须指明您的排队任务与哪个 全局对象 关联;这可以确保如果该全局对象处于非活动状态,则该任务不会运行。
排队全局任务 所基于的基本原语是 排队任务 算法。一般来说,排队全局任务 更好,因为它会自动选择正确的 事件循环,并在适当的情况下选择 文档。旧规范通常将 排队任务 与 隐含事件循环 和 隐含文档 概念结合使用,但不鼓励这样做。
综上所述,我们可以为需要异步执行工作的典型算法提供一个模板:
-
并行 执行一组可能代价高昂的步骤,完全对与领域无关的值进行操作,并产生与领域无关的结果。
-
在指定的 任务来源 上并给定适当的 全局对象,排队一个全局任务,以将与领域无关的结果转换回对 事件循环 上可观察到的 JavaScript 对象世界的可观察到的影响。
以下是一个算法,它在将传入的 标量值字符串 列表 input
解析为 URL 后对其进行“加密”:
-
令
urls
为一个空的 列表。 -
对于
input
中的每个string
:-
如果
parsed
是失败的,则返回 一个被拒绝的 Promise,并带有一个 "SyntaxError
"DOMException
。 -
令
serialized
为对parsed
应用 URL 序列化器 的结果。 -
将
serialized
追加 到urls
。
-
令
realm
为 当前领域。 -
令
p
为一个新的 Promise。 -
并行 运行以下步骤:
-
返回
p
。
关于此算法,需要注意以下几点:
-
它在进入 并行 步骤之前,在 事件循环 上预先进行 URL 解析。这是必要的,因为解析取决于 当前设置对象,该对象在进入 并行 后将不再是当前的。
-
或者,它可以保存对 当前设置对象 的 API 基本 URL 的引用,并在 并行 步骤中使用它;这将是等效的。但是,我们建议改为预先完成尽可能多的工作,就像本例一样。尝试保存正确的值容易出错;例如,如果我们只保存了 当前设置对象,而不是其 API 基本 URL,则可能会出现竞争条件。
-
Promise 作为可观察到的 JavaScript 对象,永远不会在 并行 步骤中创建和操作。
p
是在进入这些步骤之前创建的,然后在专门为此目的 排队 的 任务 中进行操作。 -
JavaScript 数组对象的创建也发生在排队的任务期间,并且需要注意指定在哪个领域中创建数组,因为这在上下文中不再明显。
(关于最后两点,另请参阅 whatwg/webidl issue #135 和 whatwg/webidl issue #371,我们仍在仔细研究上述 Promise 解析模式的细节。)
还要注意的另一件事是,如果从 Web IDL 指定的操作(采用 sequence
<USVString
>)调用此算法,则会自动将作者作为输入提供的 领域 特定的 JavaScript 对象转换为与领域无关的 sequence
<USVString
> Web IDL 类型,然后我们将其视为 标量值字符串 的 列表。因此,根据您的规范的结构方式,在主 事件循环 上可能会发生其他隐式步骤,这些步骤在您准备好进入 并行 的整个过程中发挥作用。