Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add scheduler.yield() #35960

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open

Conversation

brendankenny
Copy link

Description

Chrome 129 adds yield method to the Scheduler interface. This PR adds reference documentation for the new method.

Additional details

@brendankenny brendankenny requested a review from a team as a code owner September 19, 2024 16:45
@brendankenny brendankenny requested review from wbamberg and removed request for a team September 19, 2024 16:45
@github-actions github-actions bot added Content:WebAPI Web API docs size/m [PR only] 51-500 LoC changed labels Sep 19, 2024
Copy link
Contributor

Preview URLs

Flaws (1)

Note! 4 documents with no flaws that don't need to be listed. 🎉

URL: /en-US/docs/Web/API/Scheduler/yield
Title: Scheduler: yield() method
Flaw count: 1

  • bad_bcd_queries:
    • No BCD data for query: api.Scheduler.yield
External URLs (1)

URL: /en-US/docs/Web/API/Scheduler/yield
Title: Scheduler: yield() method

Copy link
Contributor

@tunetheweb tunetheweb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall LGTM.

Have a few (non-blocking) comments where it could potentially be improved further.

@@ -23,15 +23,33 @@ In this page, we also include information about the {{domxref("Scheduling.isInpu

The Prioritized Task Scheduling API is available in both window and worker threads using the `scheduler` property on the global object.

The main API method is {{domxref('Scheduler.postTask()')}}, which takes a callback function ("the task") and returns a promise that resolves with the return value of the function, or rejects with an error.
The main API methods are {{domxref('scheduler.postTask()')}} and {{domxref('scheduler.yield()')}}. `scheduler.postTask()` takes a callback function ("the task") and returns a promise that resolves with the return value of the function, or rejects with an error. `scheduler.yield()` turns any async function into a task by yielding the main thread to the browser for other work, with execution continuing when the returned promise is resolved.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth calling out some more differences here?

  • That postTask has more options for setting priority and/or aborting the task
  • But that scheduler.yield is simpler to use as can be added without wrapping code in a function.


#### `scheduler.postTask()`

`scheduler.postTask()` creates a task with default priority [`user-visible`](#user-visible) that has a fixed priority and cannot be aborted.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe:

Suggested change
`scheduler.postTask()` creates a task with default priority [`user-visible`](#user-visible) that has a fixed priority and cannot be aborted.
`scheduler.postTask()` creates a task with an optional priority (defaulted to [`user-visible`](#user-visible)) that cannot be aborted.

And can it not be aborted? I thought that was one of the benefits of postTask over yield (see my comment above). What's https://developer.mozilla.org//en-US/docs/Web/API/Scheduler/postTask#aborting_tasks all about?

Or does this mean the default call can't be aborted (since it doesn't provide a signal argument). If so this should be reworded (appreciate you didn't write the original, but still let's fix this while in here).


Long tasks can be broken up by awaiting `scheduler.yield()`. The function returns a promise, yielding the {{Glossary('main thread')}} to allow the browser to execute other pending work—like responding to user input—if needed.

For instance, if a `change` event listener on a checkbox results in a lot of work to filter content and update the page, this means there will be no visual feedback to the user that the checkbox was checked until that work is complete. A `scheduler.yield()` can be inserted into the event listener to yield early so the checked state will be seen immediately, and then the heavier computation can be done when the yield returns.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a valid example. However, I worry about encouraging "no-op" yields.

Maybe should start with an example:

button.addEventListener("click", async () => {
  // Provide feedback that the action was received, and is being processed
  await scheduler.yield();
  // Do longer processing
});

And then move on to your example: "It may also be sufficient to have the browser default actions provide that feedback. For instance, if a change event listener on a checkbox..."


By default, `scheduler.yield()` is run with a [`'user-visible'`](/en-US/docs/Web/API/Prioritized_Task_Scheduling_API#user-visible) priority. However, the continuation from a `scheduler.yield()` has a slightly different behavior than `scheduler.postTask()` tasks of the same `priority`.

`scheduler.yield()` enqueues its task at the front of a priority level's queue, while `scheduler.postTask()` tasks go at the end (in the spec, this is defined by a task queue's [effective priority](https://wicg.github.io/scheduling-apis/#scheduler-task-queue-effective-priority)). This means that with the same priority, the `scheduler.yield()` continuation will come first, allowing additional flexibility in how tasks can be scheduled.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whhhattt! This is news to me!

Even more reason to use yield over postTask then!

@@ -23,15 +23,33 @@ In this page, we also include information about the {{domxref("Scheduling.isInpu

The Prioritized Task Scheduling API is available in both window and worker threads using the `scheduler` property on the global object.

The main API method is {{domxref('Scheduler.postTask()')}}, which takes a callback function ("the task") and returns a promise that resolves with the return value of the function, or rejects with an error.
The main API methods are {{domxref('scheduler.postTask()')}} and {{domxref('scheduler.yield()')}}. `scheduler.postTask()` takes a callback function ("the task") and returns a promise that resolves with the return value of the function, or rejects with an error. `scheduler.yield()` turns any async function into a task by yielding the main thread to the browser for other work, with execution continuing when the returned promise is resolved.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The main API methods are {{domxref('scheduler.postTask()')}} and {{domxref('scheduler.yield()')}}. `scheduler.postTask()` takes a callback function ("the task") and returns a promise that resolves with the return value of the function, or rejects with an error. `scheduler.yield()` turns any async function into a task by yielding the main thread to the browser for other work, with execution continuing when the returned promise is resolved.
The main API methods are {{domxref('scheduler.postTask()')}} and {{domxref('scheduler.yield()')}}. `scheduler.postTask()` takes a callback function (the task) and returns a promise that resolves with the return value of the function, or rejects with an error. `scheduler.yield()` turns any async function into a task by yielding the main thread to the browser for other work, with execution continuing when the returned promise is resolved.

The simplest form of the API is shown below. This creates a task with default priority [`user-visible`](#user-visible) that has a fixed priority and cannot be aborted.
#### `scheduler.yield()`

To break up long-runing JavaScript so it doesn't block the main thread, a `scheduler.yield()` can be inserted to temporarily yield the main thread back to the browser, which creates a task to continue execution where it left off.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
To break up long-runing JavaScript so it doesn't block the main thread, a `scheduler.yield()` can be inserted to temporarily yield the main thread back to the browser, which creates a task to continue execution where it left off.
To break up long-running JavaScript tasks so they don'tt block the main thread, a call to `scheduler.yield()` can be inserted to temporarily yield the main thread back to the browser, which creates a task to continue execution where it left off.

Copy link

@malchata malchata left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this generally looks fine, but I added a couple of suggestions for prose.

}
```

`scheduler.yield()` returns a promise that can be awaited for when execution can continue. This allows work that belongs in the same function to stay in the same function but without blocking the main thread the entire time the function runs.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
`scheduler.yield()` returns a promise that can be awaited for when execution can continue. This allows work that belongs in the same function to stay in the same function but without blocking the main thread the entire time the function runs.
`scheduler.yield()` returns a promise that can be awaited for when execution can continue. This allows work that belongs in the same function to stay in the same function, but without blocking the main thread the entire time the function runs.


`scheduler.yield()` returns a promise that can be awaited for when execution can continue. This allows work that belongs in the same function to stay in the same function but without blocking the main thread the entire time the function runs.

`scheduler.yield()` takes no arguments. The task that triggers its continuation has a default [`user-visible`](#user-visible) priority, but if a `scheduler.yield()` is run within a `scheduler.postTask()` task, it will [inherit the priority of the surrounding task](/en-US/docs/Web/API/Scheduler/yield#inheriting_task_priorities).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
`scheduler.yield()` takes no arguments. The task that triggers its continuation has a default [`user-visible`](#user-visible) priority, but if a `scheduler.yield()` is run within a `scheduler.postTask()` task, it will [inherit the priority of the surrounding task](/en-US/docs/Web/API/Scheduler/yield#inheriting_task_priorities).
`scheduler.yield()` takes no arguments. The task that triggers its continuation has a default [`user-visible`](#user-visible) priority, but if `scheduler.yield()` is called within a `scheduler.postTask()` callback, it will [inherit the priority of the surrounding task](/en-US/docs/Web/API/Scheduler/yield#inheriting_task_priorities).


The task can continue when the promise returned by the method is resolved. The priority for when the promise is resolved defaults to [`'user-visible'`](/en-US/docs/Web/API/Prioritized_Task_Scheduling_API#user-visible), but can inherit a different priority if the `yield()` occurs within a {{domxref('Scheduler.postTask')}}.

Similarly, the continuation of work after the `yield()` can be aborted if it occurs within a `postTask()` task and the [task is aborted](/en-US/docs/Web/API/Scheduler/postTask#aborting_tasks).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Similarly, the continuation of work after the `yield()` can be aborted if it occurs within a `postTask()` task and the [task is aborted](/en-US/docs/Web/API/Scheduler/postTask#aborting_tasks).
Similarly, the continuation of work after the `yield()` can be aborted if it occurs within a `postTask()` callback, and the [task is aborted](/en-US/docs/Web/API/Scheduler/postTask#aborting_tasks).


### Basic usage

Long tasks can be broken up by awaiting `scheduler.yield()`. The function returns a promise, yielding the {{Glossary('main thread')}} to allow the browser to execute other pending work—like responding to user input—if needed.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like this glossary definition should be linked much earlier in the guide.


Long tasks can be broken up by awaiting `scheduler.yield()`. The function returns a promise, yielding the {{Glossary('main thread')}} to allow the browser to execute other pending work—like responding to user input—if needed.

For instance, if a `change` event listener on a checkbox results in a lot of work to filter content and update the page, this means there will be no visual feedback to the user that the checkbox was checked until that work is complete. A `scheduler.yield()` can be inserted into the event listener to yield early so the checked state will be seen immediately, and then the heavier computation can be done when the yield returns.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
For instance, if a `change` event listener on a checkbox results in a lot of work to filter content and update the page, this means there will be no visual feedback to the user that the checkbox was checked until that work is complete. A `scheduler.yield()` can be inserted into the event listener to yield early so the checked state will be seen immediately, and then the heavier computation can be done when the yield returns.
For instance, if a `change` event listener on a checkbox results in a lot of work to filter content and update the page, this means there will be no visual feedback to the user that the checkbox was checked until that work is complete. A `scheduler.yield()` can be inserted into the event listener to yield early so the checked state will be seen immediately, and then the remainder of the work can be done when the yield returns.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Content:WebAPI Web API docs size/m [PR only] 51-500 LoC changed
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants