1 of 14

hasPendingUserInput

tdresser@chromium.org

2 of 14

shouldYield → hasPendingUserInput

3 of 14

The Problem

Third party script wants to execute lengthy script.

High priority loading work needs to get done fast.

How do you:

  1. Get your work done quickly
  2. Without getting interrupted by third party work
  3. While being responsive to input

4 of 14

Concrete Example

A server side rendered page sends down some great looking HTML.

It then executes a bunch of script, attaching event handlers etc.

There’s third party script on the page, which wants to initialize ads.

How can this page:

  • Finish registering event handlers quickly
  • Without getting interrupted by ad loading
  • While being responsive to input

5 of 14

hasPendingUserInput

Returns true if there’s user input queued.

6 of 14

let taskQueue = [task1, task2, ...];��function doWork() {� while (let task = taskQueue.pop()) {� task.execute();� if (hasPendingUserInput()) {� setTimeout(doWork, 0);� break;� }� }�}��doWork();

7 of 14

hasPendingUserInput

How do you:

  1. Get your work done quickly
  2. Without getting interrupted by third party work
  3. While being responsive to input

If there’s no input, work is executed contiguously, with no room for third party work to interrupt. If input shows up, we can respond immediately.

Third party work can interrupt when input occurs.

8 of 14

Metrics Impact

What should long tasks report for a task which has a call to hasPendingUserInput() within it?

Proposal: Report as though the task was split at that point.

This provides a lower bound on how long the task could be blocking input.

9 of 14

Alternative: oldestInputQueuedTime

Benefit: Additional information to use when deciding when to yield.

Perhaps we should only yield if input has been blocked for > 100ms?

  • Is this a slippery slope?
  • How much information about the event is needed to make this decision?
  • How would metrics work in this context?

10 of 14

Alternative: inputPendingMoreThan(ms)

To provide the flexibility of oldestInputQueuedTime, while enabling metrics to reflect use of the API, we could instead provide a method:

inputPendingMoreThan(ms)

Which returns true if the oldest input has been queued longer than ms.

11 of 14

Proposal

Exposing time input has been queued for adds non-trivial complexity.

Based on the use cases I’m aware of, it doesn’t seem worth.��I propose hasPendingUserInput, unless we have a compelling use case for input queueing time, in which case I recommend inputPendingMoreThan.

12 of 14

Work happening during animations.

  • hasPendingUserInput doesn’t work during animations
    • Need to yield once per animation frame, to enable frame production at 60fps.
  • We could introduce hasPendingAnimationFrame
    • Or timeToAnimationFrame() => ms
    • Or animationFrameStartingWithin(ms)
  • Ideally APIs would be parallel
    • There is natural asymmetry, as we only know in advance for rAF, not input.
  • Knowing in advance enables real use-cases for providing more than a binary signal. “Do I have time to run this 2ms task before rAF?”

13 of 14

What if we’re in a rAF?

  • It could be useful to know if we’re within a rAF.

14 of 14

Proposal

  • hasPendingUserInput() => bool
  • timeToAnimationFrame() => ms
    • This isn’t a problem for metrics, as frames occur regularly, unlike user input.