---
title: "JavaScript Scheduler API: Prioritize Tasks with postTask()"
description: "scheduler.postTask() runs JavaScript at user-blocking, user-visible or background priority, giving finer control than setTimeout(0) over when work runs."
canonical: "https://www.trevorlasn.com/blog/javascript-scheduler-api"
author: "Trevor I. Lasn"
published: "2024-11-26"
last-updated: "2026-02-15"
topics: ["JavaScript", "Web performance"]
---

# JavaScript Scheduler API: Prioritize Tasks with postTask()

scheduler.postTask() runs JavaScript at user-blocking, user-visible or background priority, giving finer control than setTimeout(0) over when work runs.

By Trevor I. Lasn · Published 2024-11-26 · Updated 2026-02-15 · https://www.trevorlasn.com/blog/javascript-scheduler-api

The [JavaScript Scheduler API](https://wicg.github.io/scheduling-apis/) introduces a standardized approach to managing task prioritization in web apps. 

[Browser support for `scheduler` on webstatus.dev](https://webstatus.dev/features/scheduler)

JavaScript devs have historically relied on `setTimeout(0)` to yield to the main thread, this new API provides more precise control over how and when tasks execute.

The traditional `setTimeout` approach to yielding the main thread lacks granular control and proper prioritization. The Scheduler API addresses these shortcomings by offering a more robust solution.

```javascript title="JavaScript"
// Old approach with setTimeout
setTimeout(() => {
  processNextBatch();
}, 0);
```

The Scheduler API offers two primary methods for better control: `yield()` and `postTask()`. The simpler `yield()` method provides a clean way to pause execution.
```javascript title="JavaScript"
// New approach with scheduler.yield()
await scheduler.yield();
processNextBatch();
```

For more complex scenarios, `postTask()` enables priority-based scheduling:

```javascript title="JavaScript"
await scheduler.postTask(() => { 
  processNextBatch() }, 
  { priority: 'background' }
);
```

<br />

### Task Priority Levels

The Scheduler API supports three distinct priority levels for tasks: `user-blocking`, `user-visible`, and `background`. Tasks run in priority order, then by the sequence they were added to the scheduler queue.

```ts title="TypeScript"
enum TaskPriority {
  "user-blocking",
  "user-visible",
  "background"
};
```

The priority level determines when and how urgently the browser should execute your task. Here's what each priority means in practice.

- `user-blocking`: Tasks that block the main thread and require immediate attention. For example, handling user input or updating the UI.
- `user-visible`: Tasks that are important but not as urgent as `user-blocking` tasks. For example, fetching data for the next screen.
- `background`: Tasks that can run in the background without affecting the user experience. For example, preloading assets or performing analytics.

### Managing Multiple Tasks with Priorities

The Scheduler API excels at managing multiple tasks with different priorities, ensuring critical operations complete first while deferring less important work. When multiple tasks run simultaneously, the browser executes them in priority order first, then by their queuing sequence.

```javascript title="JavaScript"
async function processData(data) {
  const controller = new TaskController({ priority: 'background' });

  try {
    await scheduler.postTask(
      () => heavyComputation(data),
      { 
        signal: controller.signal,
        priority: 'background'
      }
    );
  } catch (error) {
    if (error.name !== 'AbortError') {
      console.error('Processing failed:', error);
    }
  }
}
```

<br />

### Dynamic Priority Changes

Tasks can change priority during execution using `TaskController`. This becomes valuable when the importance of a task changes based on user interaction or system state.

```javascript title="JavaScript"
const controller = new TaskController({ priority: 'background' });

controller.signal.addEventListener('prioritychange', (event) => {
  console.log(`Priority changed from ${event.previousPriority} to ${event.target.priority}`);
});

scheduler.postTask(
  async () => {
    // Start as background task
    await processInitialData();
    
    // Upgrade priority when needed
    controller.setPriority('user-visible');
    await processCriticalData();
  },
  { signal: controller.signal }
);
```

<br />

### Aborting Tasks

The Scheduler API allows task abortion through both `TaskController` and `AbortController`

```javascript title="JavaScript"
// Basic task abortion
const controller = new TaskController();

scheduler.postTask(
  async () => {
    const response = await fetch('https://api.example.com/data');
    return response.json();
  },
  { signal: controller.signal }
).catch(error => {
  if (error.name === 'AbortError') {
    console.log('Task was aborted');
  }
});

// Abort the task
controller.abort();
```

<br />

### Browser Support and Fallback Pattern

Before using the Scheduler API, check if the browser supports it. Create a wrapper that falls back to `setTimeout` when needed.

```javascript title="JavaScript"
function createScheduler() {
  if ('scheduler' in window) {
    return {
      async schedule(task, options = {}) {
        return scheduler.postTask(task, options);
      },
      async yield() {
        return scheduler.yield();
      }
    };
  }

  return {
    async schedule(task, { delay = 0 } = {}) {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          try {
            resolve(task());
          } catch (error) {
            reject(error);
          }
        }, delay);
      });
    },
    async yield() {
      return new Promise(resolve => setTimeout(resolve, 0));
    }
  };
}

/* 
Use this fallback pattern to ensure your application works 
across all browsers while taking 
advantage of the Scheduler API where available.
*/

const taskScheduler = createScheduler();

async function processData(data) {
  try {
    await taskScheduler.schedule(
      () => heavyComputation(data),
      { priority: 'background' }
    );
  } catch (error) {
    console.error('Processing failed:', error);
  }
}
```

The Scheduler API continues to evolve. The `yield()` method is now supported in Chrome and Firefox (since August 2025), though Safari has yet to implement either `postTask()` or `yield()`. The API's design allows for future extensions while maintaining backward compatibility.

This standardized approach to task scheduling represents a significant step forward for web application performance. It enables more responsive interfaces and better resource utilization without relying on implementation-specific hacks or workarounds.

The Scheduler API solves fundamental problems in web application performance. Its clear prioritization model and abort capabilities provide the tools needed for building more responsive applications. As browser support expands, this API will become an essential part of the web development toolkit.
