This post introduces the scheduler.yield
API and explains how you can use it to optimise the performance of your web applications. There are also interactive code demos you can follow along with.
Introduction
In the context of web performance, scheduling is the browser's way of deciding which tasks to run (JavaScript, page rendering, etc.) and when to run them. Scheduling is a key concept that can impact the user experience of your web applications.
While not every website needs to worry about scheduling, scheduling is an important feature to understand as it offers precise timing and control over how your JavaScript is scheduled.
What is scheduler.yield
?
The scheduler.yield
API is supported in Chromium-based browsers.
The scheduler.yield()
method is used to yield control back to the browser's scheduler, allowing other important tasks to run. It's the browser that decides what's considered "important".
scheduler.yield(); // Promise {<pending>}
This can be useful when you want to ensure that your JavaScript code doesn't block the main thread and negatively impact the user experience.
You might be wondering, what other work does the browser need to do? The browser scheduler is responsible for a lot of things, such as:
- Rendering the page
- Garbage collection
- Handling user input
Let's focus on the last one, handling user input. When the main thread is blocked, the browser can't respond to most types of user input. This can lead to a poor user experience and result in poor web performance metric scores, like Interaction to Next Paint as part of Core Web Vitals.
In the following diagram, the "Before scheduler.yield" and "After scheduler.yield" examples both take 3.5 seconds to complete. However, the "After scheduler.yield" version is able to run an important task related to user input after 1 second, while the "Before scheduler.yield" example is only able to run the important task after 3 seconds.
Understanding key web performance concepts
If you're finding terms like "main thread," "browser scheduler," and "blocking" confusing - don't worry! You're not alone.
While web performance used to focus mostly on network speed (how fast resources download), today's JavaScript-heavy web apps have shifted our attention to runtime performance (how smoothly the browser executes code).
Here's a breakdown of these important concepts:
-
Main thread: Think of this as the browser's primary worker that handles most tasks - running your JavaScript, processing user input, and painting the screen.
-
Browser scheduler: This is like a traffic controller that decides which tasks to run and when. It's responsible for ensuring that important tasks (like responding to user input) are prioritized.
-
Blocking: When a task (like a heavy JavaScript calculation) is running on the main thread, it prevents other important work from happening - like responding to a user's click.
When we talk about scheduler.yield
, we're giving that traffic controller a chance to prioritize more urgent tasks before continuing with our code.
Note on code examples
In the next few sections, you'll see code examples that demonstrate how to use scheduler.yield
. Before that however, we first need an example of a slow running function that blocks the main thread. The implementation of the following function isn't important:
// A helper function to block the main thread
// for demonstration purposes only
function blockMainThread(duration) {
const startTime = Date.now();
while (Date.now() - startTime < duration) {
// Blocking the main thread
}
}
The takeaway here is that when blockMainThread
is called, it blocks the main thread for (roughly) the specified duration.
How to use the scheduler.yield
API
In this section, you'll see a minimal before and after example of using scheduler.yield
.
A task that blocks for 50ms or longer is known as a Long Task and needs fixing.
First, the "before" which blocks the main thread for 1 second, and does not use scheduler.yield
:
Before:
// This function blocks for 1 continuous second (500ms + 500ms)
function blocksContinuously() {
blockMainThread(500);
blockMainThread(500);
}
And here's the "after" which does uses scheduler.yield
:
After:
async function blocksInChunks() {
// Blocks for 500ms, then yields to the browser scheduler
blockMainThread(500);
await scheduler.yield(); // The browser scheduler can run other tasks at this point
// Blocks for another 500ms and returns
blockMainThread(500);
}
And here's the difference shown in a DevTools Performance profile:
Here are some key points to note from the Chrome DevTools screenshot:
Without scheduler.yield | With scheduler.yield | |
---|---|---|
Button responsiveness | The button :active state freezes for 1 second | The button active state freezes for 500ms |
User experience | The user is unable to interact with the page for 1 second | The user can interact with the page after 500ms |
Long Task | The Long Task takes 1 second to complete | The Long Task takes 500ms to complete |
scheduler.yield
returns a promise. After the promise is resolved, the remaining code in blocksInChunks
will run.
There's a big difference between the two implementations shown earlier:
- Without
scheduler.yield
: TheblocksContinuously
function blocks the main thread for 1 continuous second. - With
scheduler.yield
: TheblocksInChunks
function blocks for 500ms, yields to the browser scheduler, and then blocks for another 500ms.
Most websites will have JavaScript that occasionally blocks the main thread - that's normal. But there's a huge difference between:
- Blocking the main thread for 1 continuous second (bad user experience)
- Blocking for 500ms, yielding to let the browser handle important tasks, then continuing with another 500ms (much better experience)
For best results, you should break down your work into even smaller chunks and yield between them. This gives the browser frequent opportunities to handle user interactions.
Our simple examples above demonstrate the basic concept, but you might wonder: "How does this look in practice when a user tries to interact with my page?" Let's see this in action with some real examples.


Monitor Page Speed & Core Web Vitals
DebugBear monitoring includes:
- In-depth Page Speed Reports
- Automated Recommendations
- Real User Analytics Data
Demo: using scheduler.yield
with a user interaction
Here's a interactive demo of using scheduler.yield
that highlights a user interaction scenario.
Follow these steps to see how using scheduler.yield results in a faster interaction:
- Choose either "One Long Task" or "Many Smaller Tasks" button.
- One Long Task: Does not use
scheduler.yield
. - Many Smaller Tasks: Uses
scheduler.yield
.
- Immediately after clicking one of those buttons, quickly click the "Accept Cookies" button.
- Observe the delay in being notified "Here are your cookies"
Bonus: Record a DevTools performance profile of the above steps. Can you observe how JavaScript execution appears differently depending on whether you clicked "One Long Task" or "Many Smaller Tasks"? If not, continue reading to see what it looks like.
In this scenario, there is a long blocking task that does not use scheduler.yield
. During the task, the user clicks on an "Accept cookies" button. The browser is unable to respond to this user input until the long task is complete.
Note if you are not sure on what to focus on within the following screenshot, skip it, and look at the next one.
Here's an annotated version of the previous screenshot:
In this next scenario, the long blocking task has been split up into chunks, and scheduler.yield
is used throughout. During the task, the user clicks on an "Accept cookies" button. The browser is able to respond to this user input after the first chunk of work is complete.
Note if you are not sure on what to focus on within the following screenshot, skip it, and look at the next one.
Here's an annotated version of the previous screenshot:
You can install the Site Speed by DebugBear Extension to see an overlay of Interaction to Next Paint (INP) times on your website, amongst other metrics.
Browser support
scheduler.yield
is available in Chromium based browsers like Chrome and Edge.
For other browsers, you can use this JavaScript check to see if scheduler.yield
is available:
if ("scheduler" in window && "yield" in scheduler) {
console.log("scheduler.yield is supported");
} else {
console.log("scheduler.yield is not supported");
}