使用 isInputPending() 改善 JS 调度

一种新的 JavaScript API,可帮助您避免在加载性能和输入响应速度之间进行权衡取舍。

Nate Schloss
Nate Schloss
Andrew Comminos
Andrew Comminos

加载速度太快很难。目前利用 JS 呈现内容的网站 需要在加载性能和输入 响应速度:执行展示广告所需的所有工作 (提高加载性能,降低输入响应速度),或者 将工作分成数个较小的任务, 输入和绘制(加载性能较差,输入较好 响应速度)。

为了不需要进行这种取舍,Facebook 建议并实施了 isInputPending() Chromium 中的 API 来提升响应速度, 收益。根据源试用反馈,我们对 API,并且很高兴地宣布,现在 Chromium 中会默认提供该 API 87!

浏览器兼容性

浏览器支持

  • Chrome:87。 <ph type="x-smartling-placeholder">
  • Edge:87。 <ph type="x-smartling-placeholder">
  • Firefox:不支持。 <ph type="x-smartling-placeholder">
  • Safari:不支持。 <ph type="x-smartling-placeholder">

来源

从版本 87 开始,基于 Chromium 的浏览器已提供 isInputPending()。 其他浏览器均未发出发送此 API 的信号。

背景

在当今的 JS 生态系统中,大部分工作都在一个线程(即主线程)上完成。 这为开发者提供了一个强大的执行模型,但用户体验 (特别是响应速度)如果脚本执行时间过长,可能会受到很大影响 。如果页面在触发输入事件的同时执行大量工作, 例如,在处理点击输入事件之前, 。

当前的最佳做法是通过违反 将 JavaScript 拆分成更小的代码块。在网页加载过程中,网页可以运行 然后将控制权传递给浏览器。通过 然后,浏览器可以检查其输入事件队列, 需要向网页传达相关信息然后,浏览器会返回到运行 JavaScript 会在添加时被阻止。这样做有帮助,但可能会导致其他问题。

每当网页将控制权交还给浏览器时, 检查其输入事件队列,处理事件,并从 JavaScript 代码块。浏览器响应事件的速度越快, 网页加载速度变慢如果我们收益太频繁, 加载太慢。如果收益降低,浏览器需要更长的时间 响应用户事件,令用户感到失望。没意思。

一张示意图,显示了当您运行长 JS 任务时,浏览器分派事件的时间会减少。

在 Facebook,我们想了解 新的加载方法,可消除这种令人沮丧的权衡。周三 就此向我们在 Chrome 浏览器上的朋友们提出 建议 价格为 isInputPending()isInputPending() API 是率先采用 会中断用户在网络上的输入,并允许 JavaScript 能够检查输入,而不让给浏览器。

显示 isInputPending() 允许 JS 检查是否存在待处理的用户输入的示意图,而不会将执行完全交回给浏览器。

由于大家都对 API 很感兴趣,因此我们与 Chrome 公司的同事通力合作 ,以便在 Chromium 中实现并发布该功能。在 Chrome 的帮助下 我们让这些补丁通过源试用进行 (Chrome 可通过此方式测试更改并获取开发者的反馈 然后再全面发布 API)。

现在,我们已听取来自源试用和 W3C 网络性能工作组并实施了对 API 的更改。

示例:收益器调度器

假设您需要执行一系列阻止显示的工作来加载 例如,根据组件生成标记、过滤出质数,或 只需绘制一个炫酷的加载旋转图标即可其中每个过程都分解为 工作项。让我们使用调度器模式草拟一下 我们将使用一个假设的 processWorkQueue() 函数完成工作:

const DEADLINE = performance.now() + QUANTUM;
while (workQueue.length > 0) {
  if (performance.now() >= DEADLINE) {
    // Yield the event loop if we're out of time.
    setTimeout(processWorkQueue);
    return;
  }
  let job = workQueue.shift();
  job.execute();
}

稍后通过 setTimeout() 在新的宏任务中调用 processWorkQueue(),我们 让浏览器能够对输入保持一定程度的响应( 运行事件处理脚本) 不会出现任何中断。不过,我们可能会因其他工作而需要安排较长时间 需要控制事件循环,或者最多额外获得 QUANTUM 毫秒 事件延迟时间

没关系,但我们可以做得更好吗?当然可以!

const DEADLINE = performance.now() + QUANTUM;
while (workQueue.length > 0) {
  if (navigator.scheduling.isInputPending() || performance.now() >= DEADLINE) {
    // Yield if we have to handle an input event, or we're out of time.
    setTimeout(processWorkQueue);
    return;
  }
  let job = workQueue.shift();
  job.execute();
}

通过引入对 navigator.scheduling.isInputPending() 的调用,我们能够 更快地响应输入,同时仍能确保我们的显示阻止工作 否则就会不中断执行如果我们不感兴趣 在完成工作之前,除了输入(例如绘制)之外, QUANTUM 的长度。

默认情况下,设为“连续”事件不会从 isInputPending() 返回。这些 包括mousemovepointermove等。如果您希望通过广告 也没问题使用以下代码向 isInputPending() 提供一个对象: includeContinuous 设置为 true,就可以开始了:

const DEADLINE = performance.now() + QUANTUM;
const options = { includeContinuous: true };
while (workQueue.length > 0) {
  if (navigator.scheduling.isInputPending(options) || performance.now() >= DEADLINE) {
    // Yield if we have to handle an input event (any of them!), or we're out of time.
    setTimeout(processWorkQueue);
    return;
  }
  let job = workQueue.shift();
  job.execute();
}

大功告成!React 等框架将 isInputPending() 支持内置到 使用类似逻辑的核心调度库。我们希望这样能 开发者使用这些框架能够从 isInputPending() 中受益 而无需大幅度重写。

让步不一定是坏事

值得注意的是,产量减少并不是适合所有用途的理想解决方案 这种情况。除了将控制权交还给浏览器之外,还有很多原因 处理输入事件,例如执行渲染和 页面。

在某些情况下,浏览器无法正确归因待处理 输入事件。特别是,为跨源设置复杂的裁剪和遮罩 iframe 可能会报告假负例(即 isInputPending() 可能会意外返回 false)。确保收益足够频繁 你的网站需要与风格化的子框架互动

也要注意共享同一事件循环的其他网页。在 与 Chrome(Android 版)一样,多个源共享一个活动 循环。如果将输入分派给isInputPending()true 因此后台网页可能会干扰 前景网页的响应速度。您可能希望降低、推迟或 使用 Page Visibility API 在后台运行时,显示频率会更高。

我们建议您谨慎使用 isInputPending()。如果没有 阻止用户执行某些工作,然后在事件循环中 生成频率更高耗时较长的任务可能会造成负面影响

反馈

总结

我们很高兴地宣布,isInputPending() 即将发布,开发者可以 马上开始使用吧!该 API 是 Facebook 首次使用 并将其从创意孵化阶段推进到标准提案, 。衷心感谢所有帮助我们实现这一目标的所有人 特别感谢所有帮助我们完善 Chrome 的 Chrome 员工 然后发货!

Will H McMahan 拍摄的主打照片 不启动