isInputPending Faster input events with Facebook's first browser API contribution

UPDATE Nov. 19, 2020: As of Chrome 87, the IsInputPending API is now available without the origin trial. The latest API incorporates a number of updates we’ve made in response to feedback from the origin trial, as well as from other members of the W3C Web Performance working group.

The time it takes for an interaction to go from the user input event (such as clicking a button or typing in a box) that triggered it to being completely rendered is an important web metric. At Facebook, we measure events at four stages: the moment the operating system gets the input, the moment we actually start processing it, the moment we start showing changes on the screen in response to the event, and the moment we finish processing it. As we looked at our top interactions, we noticed that queueing time was causing one of the biggest delays. Queuing refers to the time between when a person interacts with a page (e.g., clicks or types) and when we actually start processing the event. In extreme cases, this delay can be frustrating. Imagine clicking a notifications icon, then having to wait a full minute for the button to indicate that it registered your click. You probably wouldn’t wait for it to finish loading.

Our position running a widely used website — and our work supporting the popular React framework — gives us unique insight into the browser platform, and we wanted to utilize that to address the queueing time issue. As an active part of the web standards community (we are W3C members), we have been involved in numerous standards, including service workers and CSS scrolling, but until recently, we’d never created an API proposal to contribute code to a web browser. We felt we could make a significant improvement, so we proposed a new API and worked closely with our counterparts at Google Chrome to contribute code for an origin trial. We are excited to share that the Chrome 74 release will include the origin trial for our isInputPending API, which can be used to improve both overall JavaScript execution time and event response times. This origin trial is a first step to improve scheduling JavaScript on the web. We hope to take developer feedback from this trial and use it to make the case for fully shipping the API.

Load fast or respond fast: Pick one

On the web, there is generally a hard trade-off between loading a page quickly and responding to events quickly. If a website requires JavaScript, one strategy is to run it all in one uninterrupted block. But this can create issues. The JavaScript engines inside web browsers are generally single threaded, meaning they can do only one thing on the page at a time. In the case of page load, this means if a user clicks on something while the browser is running JavaScript, the browser has to queue the click input event until the entire JavaScript block is run.

Like many other sites, we deal with this issue by breaking the JavaScript up into smaller blocks. While the page is loading, we run a bit of JavaScript, and then we yield and pass control back to the browser. The browser can then check its input event queue and see whether there is anything it needs to tell the page about. Then the browser can go back to running the JavaScript blocks as they get added. This helps, but it can cause other issues. Each time we yield control back to the browser, it takes some time for the browser to check its input event queue, process events, and pick up the next JavaScript block. So, while the browser responds to events more quickly, we still need to strike a balance between the code block sizes and how often we yield to the browser. If we yield too often, the page loads too slowly. If we yield less often, it takes longer for the browser to respond to user events, and people get frustrated.

If we run large blocks of JavaScript, there is a long delay before the browser can dispatch user events (top); if we run smaller blocks, the page takes longer to load (bottom).
If we run large blocks of JavaScript, there is a long delay before the browser can dispatch user events (top); if we run smaller blocks, the page takes longer to load (bottom).

isInputPending to the rescue

When we first spotted the queueing time delays, we reached out to our counterparts at Chrome. We wanted to see what things would look like if we came up with a new approach for loading that would eliminate this frustrating trade-off. After talking with them, we came up with the proposal for isInputPending. The isInputPending API is the first to use the concept of interrupts for user inputs to the web.

Under the hood, isInputPending hooks into Chrome’s compositor-side input queue to intercept events before they’re posted to the main thread. As all processing of these input events is done off the main thread, calls to isInputPending do not use many computational resources and should be very quick. This allows developers to invoke the API frequently and maximize responsiveness.

Once we had the proposal ready, we approached the W3C Web Performance Working Group (which sets web standards around performance) and got consensus from various browser vendors that our idea was worth exploring. Since there was interest in the API, we partnered with our colleagues at Chrome, implemented the new API ourselves, and submitted the relevant code patches to Chrome. With help from the Chrome engineers, we got the patches landed behind an origin trial, which is a way for Chrome to test changes and get feedback from developers before fully releasing an API. The origin trial will allow us to get a sense of how important the API is to developers, which is important information for all browser vendors on the web and is will shape our future conversations about this API. This is the first time we have gone through the steps of creating a web API proposal, discussing the proposal in a standards forum, and contributing the code for that proposal to a web browser.

How isInputPending works

As the name implies, isInputPending tells you whether there is input pending. Developers can then use this information while running JavaScript to decide whether they want to yield back to the browser. When used properly, isInputPending can completely eliminate the trade-off between loading quickly and responding to events quickly.

The heart of the API is exposed via navigator.scheduling.isInputPending() . In essence, if the browser expects that an input event will be dispatched, this method returns true. When invoked without any arguments, all supported event types are checked. Alternatively, you may manually specify a list of mouse, wheel, touch, and key DOM UI event types that should be checked for pending input.

Sample: Checking for any input events

while (workQueue.length > 0) {
  if (navigator.scheduling.isInputPending()) {
    // Stop doing work if we have to handle an input event.
    break;
  }
  let job = workQueue.shift();
  job.execute();
}

Sample: Checking for specific input events

while (workQueue.length > 0) {
  if (navigator.scheduling.isInputPending(['mousedown', 'mouseup', 'keydown', 'keyup'])) {
    // Stop doing work if we think we'll start receiving a mouse or key event.
    break;
  }
  let job = workQueue.shift();
  job.execute();
}

isInputPending Faster input events with Facebook's first browser API contribution

What’s next?

If feedback is positive, isInputPending could become fully available in Chrome. We would then be able to get rid of these noticeable queueing delays and make the web experience faster and more fluid for the people on our sites. For developers who are also looking to get rid of queueing delays and improve interaction and loading performance, the origin trial will be available shortly. Register here and share your feedback on the origin trial once it begins. The registration form explains how to share feedback.

The process of bringing isInputPending to Chrome represents a new method of developing web standards at Facebook. We hope to continue driving new APIs and to ramp up our contributions to open source web browsers. Down the road, we could potentially build this API directly into React’s concurrent mode so developers would get the API benefits out of the box. In addition, isInputPending is now part of a larger effort to build scheduling primitives into the web. We look forward to continuing our collaboration with Chrome on additional scheduling APIs. Eventually, we hope to see browser tools that allow developers to integrate deeper into the browser’s task queue and even let developers give the browser insight into the priorities of various network requests and tasks.

To help personalize content, tailor and measure ads and provide a safer experience, we use cookies. By clicking or navigating the site, you agree to allow our collection of information on and off Facebook through cookies. Learn more, including about available controls: Cookie Policy