介绍scheduler.yield():Chrome用于优化INP的最新API
已发表: 2023-09-15毫无疑问,2023 年 Google 的重点是响应能力。
到目前为止,他们:
- 将交互从实验状态移至待定状态;
- 宣布 INP 将在 2024 年 3 月取代首次输入延迟,成为新的核心 Web 响应能力指标;
- 开始在 Search Console 中标记 INP 问题,并向未达到良好响应阈值的网站发送电子邮件。
现在,Chrome 团队宣布他们正在对新的调度程序 API – Scheduler.yield()进行原始试验。
Scheduler.yield()预计将帮助开发人员提高网站的响应能力,为他们提供一种更简单、更好的方法来将控制权交还给主线程。
请继续阅读,了解有关新 API 以及如何在您的网站上试用的更多信息。
快速回顾长任务和主线程
如果您很清楚任务和主线程是什么,请随意跳过这一部分。 如果没有,我们鼓励您阅读这篇快速回顾,因为它是理解Scheduler.yield()以及如何实现它的基础。
浏览器作为工作所做的一切都被视为任务。 这包括渲染、解析 HTML 和 CSS、运行您编写的 JavaScript 代码以及您可能无法直接控制的其他事情。
主线程是浏览器完成大部分工作的地方。
不幸的是,主线程一次只能处理一个任务。 如果一个任务运行时间超过 50 毫秒,则被认为是一个长任务。
遇到长任务意味着浏览器将根据需要运行它来完成它。 完成后,控制权将交还给主线程,允许浏览器处理队列中的下一个任务。
长任务是页面响应能力差的主要原因,因为它们延迟了浏览器响应用户输入的能力。 此外,JavaScript 及其运行到完成模型是阻塞主线程的罪魁祸首。
这就是为什么它被认为是渲染阻塞资源——当浏览器遇到它时,它必须在执行其他操作之前下载、解析和执行它。
好消息是,仅仅因为您的代码在浏览器中启动任务并不意味着您必须等到该任务完成才能将控制权返回到主线程。
您可以通过在任务中显式让出来分解长任务。
简而言之,任务让出可确保浏览器不会过于专注于一项任务,从而错过或延迟响应其他重要任务或用户交互。
不幸的是,当前的收益策略并不完美……
为什么使用scheduler.yield():当前产量策略的问题
屈服于主线程并不是一个新概念。 相当长一段时间以来,开发人员一直在使用不同的收益策略来分解长任务:
1.setTimeout()
setTimeout() 允许您安排任务在指定的延迟后或定期运行。 即使您指定超时为 0,这也会将回调的执行推迟到单独的任务中。当您有多个函数需要一个接一个地运行时,此方法非常有效。
缺点:精度无法保证。 由于队列中的其他任务,回调可能不会在指定的延迟之后准确运行。 此外,如果您正在循环中处理大量数据集,则该任务可能会变得非常耗时,尤其是在处理数百万个条目时。
2. 请求空闲回调()
requestIdleCallback() 允许您安排任务在浏览器可能有的任何空闲期间运行。 它对于执行非紧急任务而不影响用户体验非常有用。
缺点: requestIdleCallback()以尽可能低的优先级调度任务,这意味着如果主线程拥塞,调度的任务可能永远无法运行。
3.isInputPending()
isInputPending() 可以随时执行以检查用户是否正在尝试与页面上的元素交互。 如果是,该函数返回true ; 如果不是,则返回false 。
想象一下,您有一系列任务要执行,但不想中断用户交互。 您可以使用isInputPending()和yieldToMain()函数来确保用户输入在与页面交互时不会延迟。
缺点: isInputPending()可能并不总是在用户输入后立即返回 true。 这是因为操作系统需要时间来告诉浏览器交互发生了。 这意味着其他代码可能已经开始执行。

这些是返回主线程的一些流行方法。 如您所见,每种方法都有其自身的缺点。
但最显着的缺点是:
当您通过推迟代码在后续任务中运行来屈服于主线程时,该代码将被添加到任务队列的最末尾。
为什么这是一个问题?
这是一个三重答案:
- 增加逻辑错误的机会:由于延迟代码被放置在任务队列的末尾,因此浏览器在返回延迟任务之前可能会执行其他任务。 这可能会影响函数的执行顺序,并可能导致逻辑错误或意外行为。
- 执行延迟:如果队列中有很多任务,则浏览器可能需要花费大量时间才能到达并执行延迟的代码。
- 不可预测性:很难准确预测延迟任务何时运行,因为它取决于队列中已有任务的数量和性质。 这种不可预测性可能会给调试和性能优化带来挑战。
总之,虽然使用当前的策略屈服于主线程可以帮助维护响应式用户界面,但它也会在确保代码及时有序执行方面带来挑战。
介绍scheduler.yield()
Chrome 运行 Scheduler.yield() 的原始试验令人兴奋,因为它是一个调度程序 API,可以解决其他生成策略的所有缺点。
最重要的是,它是一个解决方案,使开发人员和所有者能够实现响应式网站和良好的 INP 分数,同时无缝执行其余代码。
那么关于Scheduler.yield()的炒作到底是什么呢?
对于初学者来说, scheduler.yield()是一个专用的yield 函数。 例如,setTimeout() 用于分解长任务并让出主线程,但它更多的是函数副作用而不是默认选项。
其次, scheduler.yield()将剩余的工作发送到队列的前面。 这意味着您想要在屈服后立即恢复的工作不会让位于其他来源的任务。
简单地说:
Scheduler.yield()为您提供了两全其美的功能 – 您可以通过屈服来提高站点的响应能力和 INP 分数,并确保您在屈服后想要完成的工作不会被延迟。
如何尝试新的调度程序 API
从 Chrome 115 开始,您可以自行测试 Scheduler.yield。
要试验新的 API,只需按照 Google 的说明操作即可:
- 如果您想在本地试验 Scheduler.yield,请在 Chrome 的地址栏中输入chrome://flags ,然后从“实验性 Web 平台功能”部分的下拉列表中选择“启用”。 这将使 Scheduler.yield (以及任何其他实验性功能)仅在您的 Chrome 实例中可用。
- 如果您想在可公开访问的源上为真正的 Chromium 用户启用 Scheduler.yield,则需要注册 Scheduler.yield 源试用版。 这使您可以在给定的时间内安全地试验建议的功能,并为 Chrome 团队提供有关如何在现场使用这些功能的宝贵见解。 有关原产地试验如何运作的更多信息,请阅读本指南。
测试后,您还可以提供有关如何改进它的反馈。
安全测试!
NitroPack 如何帮助解锁主线程
将长任务分解为更小的块对于为用户提供快捷的体验至关重要。
但如果您能够先发制人地优化一些繁重的 JavaScript,不是更好吗?
这就是 NitroPack 的用武之地。
凭借其35 多项高级 Web 性能功能,NitroPack 帮助全球 180,000 多个网站实现卓越的用户体验、核心 Web 生命力和转化率。
NitroPack 最显着的优势之一是它处理 JavaScript 执行的方式。
安装 NitroPack 后,我们的服务会延迟非关键资源的加载,直到检测到用户交互。
此外,得益于我们专有的资源加载机制,NitroPack 可以重新安排将资源提供给主线程的方式。 我们这样做是为了通过从主线程卸载任务来利用现代 CPU 的多核特性。
这样,我们可以保证您的主线程保持畅通并可用于处理用户交互。