Skip to content

Commit

Permalink
Add warnings on isInputPending (#36051)
Browse files Browse the repository at this point in the history
* Add warnings on isInputPending

* Apply suggestions from code review

Co-authored-by: Chris Mills <chrisdavidmills@gmail.com>

* Linting

---------

Co-authored-by: Chris Mills <chrisdavidmills@gmail.com>
  • Loading branch information
tunetheweb and chrisdavidmills authored Sep 26, 2024
1 parent a038fd9 commit 2503df3
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 50 deletions.
28 changes: 10 additions & 18 deletions files/en-us/learn/performance/javascript/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,30 +208,22 @@ async function main() {
}
```

To improve this further, we can use {{domxref("Scheduling.isInputPending", "navigator.scheduling.isInputPending()")}} to run the `yield()` function only when the user is attempting to interact with the page:
To improve this further, we can use {{domxref("Scheduler.yield")}} where available to allow this code to continue executing ahead of other less critical tasks in the queue:

```js
async function main() {
// Create an array of functions to run
const tasks = [a, b, c, d, e];

while (tasks.length > 0) {
// Yield to a pending user input
if (navigator.scheduling.isInputPending()) {
await yield();
} else {
// Shift the first task off the tasks array
const task = tasks.shift();

// Run the task
task();
}
function yield() {
// Use scheduler.yield() if available
if ("scheduler" in window && "yield" in scheduler) {
return scheduler.yield();
}

// Fall back to setTimeout:
return new Promise((resolve) => {
setTimeout(resolve, 0);
});
}
```

This allows you to avoid blocking the main thread when the user is actively interacting with the page, potentially providing a smoother user experience. However, by only yielding when necessary, we can continue running the current task when there are no user inputs to process. This also avoids tasks being placed at the back of the queue behind other non-essential browser-initiated tasks that were scheduled after the current one.

## Handling JavaScript animations

Animations can improve perceived performance, making interfaces feel snappier and making users feel like progress is being made when they are waiting for a page to load (loading spinners, for example). However, larger animations and a higher number of animations will naturally require more processing power to handle, which can degrade performance.
Expand Down
7 changes: 6 additions & 1 deletion files/en-us/web/api/navigator/scheduling/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ browser-compat: api.Navigator.scheduling

The **`scheduling`** read-only property of the {{domxref("Navigator")}} interface returns a {{domxref("Scheduling")}} object for the current document, which provides methods and properties to control scheduling tasks.

> [!WARNING]
> The {{domxref("Scheduling")}} interface (which includes the {{domxref("Scheduling.isInputPending()", "isInputPending()")}} method) has been superseded by the {{domxref("Scheduler")}} interface, the features of which are better designed for addressing scheduling tasks. See [Don't use `isInputPending()`](https://web.dev/articles/optimize-long-tasks#isinputpending) for more details.
## Value

A {{domxref("Scheduling")}} object.
Expand All @@ -30,6 +33,8 @@ See the {{domxref("Scheduling.isInputPending()")}} page for a full example.

## See also

- {{domxref("Scheduler")}} interface
- {{domxref("Prioritized_task_scheduling_api", "Prioritized Task Scheduling API")}}
- [Faster input events with Facebook's first browser API contribution](https://engineering.fb.com/2019/04/22/developer-tools/isinputpending-api/) on engineering.fb.com (2019)
- [Better JS scheduling with isInputPending()](https://developer.chrome.com/docs/capabilities/web-apis/isinputpending) on developer.chrome.com (2020)
- [Optimizing long tasks](https://web.dev/articles/optimize-long-tasks#yield_only_when_necessary) on web.dev (2022)
- [Optimizing long tasks](https://web.dev/articles/optimize-long-tasks) on web.dev (2022)
40 changes: 11 additions & 29 deletions files/en-us/web/api/prioritized_task_scheduling_api/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ The [task priorities](#task_priorities) are very coarse-grained and based around

The API is promise-based and supports the ability to set and change task priorities, to delay tasks being added to the scheduler, to abort tasks, and to monitor for priority change and abort events.

In this page, we also include information about the {{domxref("Scheduling.isInputPending", "navigator.scheduling.isInputPending()")}} method, which was defined in a different API specification but is very closely related to task scheduling. This method allows you to check whether there are pending input events in the event queue, and therefore handle task queues efficiently, only yielding to the main thread when it is needed.

## Concepts and usage

The Prioritized Task Scheduling API is available in both window and worker threads using the `scheduler` property on the global object.
Expand Down Expand Up @@ -133,10 +131,6 @@ If the priority is not set with `options.priority` or by passing a {{domxref("Ta
Note that a task that needs to be aborted must set `options.signal` to either {{domxref("TaskSignal")}} or {{domxref("AbortSignal")}}.
However for a task with an immutable priority, {{domxref("AbortSignal")}} more clearly indicates that the task priority cannot be changed using the signal.

### `isInputPending()`

The {{domxref("Scheduling.isInputPending", "isInputPending()")}} API is intended to help with task execution, enabling you to make task runners more efficient by yielding to the main thread only when the user is trying to interact with your app, rather than having to do it at arbitrary intervals.

Let's run through an example to demonstrate what we mean by this. When you have several tasks that are of roughly the same priority, it makes sense to break them down into separate functions to aid with maintenance, debugging, and many other reasons.

For example:
Expand Down Expand Up @@ -184,37 +178,27 @@ async function main() {
}
```

This helps with the main thread-blocking problem, but it could be better — we can use {{domxref("Scheduling.isInputPending", "navigator.scheduling.isInputPending()")}} to run the `yield()` function only when the user is attempting to interact with the page:
To improve this further, we can use {{domxref("Scheduler.yield")}} when available to allow this code to continue executing ahead of other less critical tasks in the queue:

```js
async function main() {
// Create an array of functions to run
const tasks = [a, b, c, d, e];

while (tasks.length > 0) {
// Yield to a pending user input
if (navigator.scheduling.isInputPending()) {
await yield();
} else {
// Shift the first task off the tasks array
const task = tasks.shift();

// Run the task
task();
}
function yield() {
// Use scheduler.yield if it exists:
if ("scheduler" in window && "yield" in scheduler) {
return scheduler.yield();
}

// Fall back to setTimeout:
return new Promise((resolve) => {
setTimeout(resolve, 0);
});
}
```

This allows you to avoid blocking the main thread when the user is actively interacting with the page, potentially providing a smoother user experience. However, by only yielding when necessary, we can continue running the current task when there are no user inputs to process. This also avoids tasks being placed at the back of the queue behind other non-essential browser-initiated tasks that were scheduled after the current one.

## Interfaces

- {{domxref("Scheduler")}}
- : Contains the {{domxref('Scheduler.postTask', 'postTask()')}} and {{domxref('Scheduler.yield', 'yield()')}} methods for adding prioritized tasks to be scheduled.
An instance of this interface is available on the {{domxref("Window")}} or {{domxref("WorkerGlobalScope")}} global objects (`globalThis.scheduler`).
- {{domxref("Scheduling")}}
- : Contains the {{domxref('Scheduling.isInputPending', 'isInputPending()')}} method for checking whether there are pending input events in the event queue.
- {{domxref("TaskController")}}
- : Supports both aborting a task and changing its priority.
- {{domxref("TaskSignal")}}
Expand All @@ -227,8 +211,6 @@ This allows you to avoid blocking the main thread when the user is actively inte
### Extensions to other interfaces

- {{domxref("Navigator.scheduling")}}
- : This property is the entry point for using the `Scheduling.isInputPending()` method.
- {{domxref("Window.scheduler")}} and {{domxref("WorkerGlobalScope.scheduler")}}
- : These properties are the entry points for using the `Scheduler.postTask()` method in a window or a worker scope, respectively.

Expand Down Expand Up @@ -530,4 +512,4 @@ Note that the second string appears in log after about 2 seconds.
## See also

- [Building a Faster Web Experience with the postTask Scheduler](https://medium.com/airbnb-engineering/building-a-faster-web-experience-with-the-posttask-scheduler-276b83454e91) on the Airbnb blog (2021)
- [Optimizing long tasks](https://web.dev/articles/optimize-long-tasks#yield_only_when_necessary) on web.dev (2022)
- [Optimizing long tasks](https://web.dev/articles/optimize-long-tasks) on web.dev (2022)
7 changes: 6 additions & 1 deletion files/en-us/web/api/scheduling/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ browser-compat: api.Scheduling

The **`Scheduling`** object provides methods and properties to control scheduling tasks within the current document.

> [!WARNING]
> The `Scheduling` interface has been superseded by the {{domxref("Scheduler")}} interface, the features of which are better designed for addressing scheduling tasks. See [Don't use `isInputPending()`](https://web.dev/articles/optimize-long-tasks#isinputpending) for more details.
## Instance methods

- {{domxref("Scheduling.isInputPending", "isInputPending()")}} {{Experimental_Inline}}
Expand All @@ -30,6 +33,8 @@ See the {{domxref("Scheduling.isInputPending()")}} page for a full example.

## See also

- {{domxref("Scheduler")}} interface
- {{domxref("Prioritized_task_scheduling_api", "Prioritized Task Scheduling API")}}
- [Faster input events with Facebook's first browser API contribution](https://engineering.fb.com/2019/04/22/developer-tools/isinputpending-api/) on engineering.fb.com (2019)
- [Better JS scheduling with isInputPending()](https://developer.chrome.com/docs/capabilities/web-apis/isinputpending) on developer.chrome.com (2020)
- [Optimizing long tasks](https://web.dev/articles/optimize-long-tasks#yield_only_when_necessary) on web.dev (2022)
- [Optimizing long tasks](https://web.dev/articles/optimize-long-tasks) on web.dev (2022)
7 changes: 6 additions & 1 deletion files/en-us/web/api/scheduling/isinputpending/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ browser-compat: api.Scheduling.isInputPending

The **`isInputPending()`** method of the {{domxref("Scheduling")}} interface allows you to check whether there are pending input events in the event queue, indicating that the user is attempting to interact with the page.

This feature is useful in situations where you have a queue of tasks to run, and you want to yield to the main thread regularly to allow user interaction to occur so that the app is kept as responsive and performant as possible. `isInputPending()` allows you to yield only when there is input pending, rather than having to do it at arbitrary intervals.
This feature can be useful in situations where you have a queue of tasks to run, and you want to yield to the main thread regularly to allow user interaction to occur so that the app is kept as responsive and performant as possible. `isInputPending()` allows you to yield only when there is input pending, rather than having to do it at arbitrary intervals.

> [!WARNING]
> The `isInputPending()` method has been superseded by features available on the {{domxref("Scheduler")}} interface such as {{domxref("Scheduler.yield()", "yield()")}}, which are better designed for addressing scheduling tasks. See [Don't use `isInputPending()`](https://web.dev/articles/optimize-long-tasks#isinputpending) for more details.
`isInputPending()` is called using `navigator.scheduling.isInputPending()`.

Expand Down Expand Up @@ -76,6 +79,8 @@ This allows you to avoid blocking the main thread when the user is actively inte

## See also

- {{domxref("Scheduler")}} interface
- {{domxref("Prioritized_task_scheduling_api", "Prioritized Task Scheduling API")}}
- [Faster input events with Facebook's first browser API contribution](https://engineering.fb.com/2019/04/22/developer-tools/isinputpending-api/) on engineering.fb.com (2019)
- [Better JS scheduling with isInputPending()](https://developer.chrome.com/docs/capabilities/web-apis/isinputpending) on developer.chrome.com (2020)
- [Optimizing long tasks](https://web.dev/articles/optimize-long-tasks#yield_only_when_necessary) on web.dev (2022)

0 comments on commit 2503df3

Please sign in to comment.