Scheduler: postTask() method

The postTask() method of the Scheduler interface is used for adding tasks to be scheduled according to their priority.

The method allows users to optionally specify a minimum delay before the task will run, a priority for the task, and a signal that can be used to modify the task priority and/or abort the task. It returns a promise that is resolved with the result of the task callback function, or rejected with the abort reason or an error thrown in the task.

Task priority can be mutable or immutable. If the task priority will never need to change then it should be set using the options.priority parameter (any priority set through a signal will then be ignored). You can still pass an AbortSignal (which has no priority) or TaskSignal to the options.signal parameter for aborting the task.

If the task priority might need to be changed the options.priority parameter must not be set. Instead a TaskController should be created and its TaskSignal should be passed to options.signal. The task priority will be initialized from the signal priority, and can later be modified using the signal's associated TaskController.

If no priority is set then the task priority defaults to "user-visible".

If a delay is specified and greater than 0, then the execution of the task will be delayed for at least that many milliseconds. Otherwise the task is immediately scheduled for prioritization.

Syntax

js

postTask(callback)
postTask(callback, options)

Parameters

callback

An callback function that implements the task. The return value of the callback is used to resolve the promise returned by this function.

options Optional

Task options, including:

priority Optional

The immutable priority of the task. One of: "user-blocking", "user-visible", "background". If set, this priority is used for the lifetime of the task and priority set on the signal is ignored.

signal Optional

A TaskSignal or AbortSignal that can be used to abort the task (from its associated controller).

If the options.priority parameter is set then the task priority cannot be changed, and any priority on the signal is ignored. Otherwise, if the signal is a TaskSignal its priority is used to set the initial task priority, and the signal's controller may later use it to change the task priority.

delay Optional

The minimum amount of time after which the task will be added to the scheduler queue, in whole milliseconds. The actual delay may be higher than specified, but will not be less. The default delay is 0.

Return Value

Returns a Promise that is resolved with the return value of the callback function, or which may be rejected with the signal's abort reason (AbortSignal.reason). The promise may also be rejected with an error thrown by the callback during execution.

Examples

The following examples are slightly simplified versions of the live examples provided in Prioritized Task Scheduling API > Examples.

Feature checking

Check whether prioritized task scheduling is supported by testing for the scheduler property in the global "this" (such as Window.scheduler).

For example, the code below logs "Feature: Supported" if the API is supported on this browser.

js

// Check that feature is supported
if ("scheduler" in this) {
  console.log("Feature: Supported");
} else {
  console.error("Feature: NOT Supported");
}

Basic usage

Tasks are posted specifying a callback function (task) in the first argument, and an optional second argument that can be used to specify a task priority, signal, and/or delay. The method returns a Promise that resolves with the return value of the callback function, or rejects with either an abort error or an error thrown in the function.

Because it returns a promise, postTask() can be chained with other promises. Below we show how to wait on the promise to resolve using then or reject using catch. The priority is not specified, so the default priority of user-visible will be used.

js

// A function that defines a task
function myTask() {
  return "Task 1: user-visible";
}

// Post task with default priority: 'user-visible' (no other options)
// When the task resolves, Promise.then() logs the result.
scheduler
  .postTask(myTask, { signal: abortTaskController.signal })
  .then((taskResult) => console.log(`${taskResult}`)) // Log resolved value
  .catch((error) => console.error("Error:", error)); // Log error or abort

The method can also be used with await inside an async function. The code below shows how you might use this approach to wait on a user-blocking task.

js

function myTask2() {
  return "Task 2: user-blocking";
}

async function runTask2() {
  const result = await scheduler.postTask(myTask2, {
    priority: "user-blocking",
  });
  console.log(result); // 'Task 2: user-blocking'.
}
runTask2();

Permanent priorities

Task priorities may be set using priority parameter in the optional second argument. Priorities that are set in this way cannot be changed (are immutable).

Below we post two groups of three tasks, each member in reverse order of priority. The final task has the default priority. When run, each task simply logs it's expected order (we're not waiting on the result because we don't need to in order to show execution order).

js

// three tasks, in reverse order of priority
scheduler.postTask(() => console.log("bckg 1"), { priority: "background" });
scheduler.postTask(() => console.log("usr-vis 1"), {
  priority: "user-visible",
});
scheduler.postTask(() => console.log("usr-blk 1"), {
  priority: "user-blocking",
});

// three more tasks, in reverse order of priority
scheduler.postTask(() => console.log("bckg 2"), { priority: "background" });
scheduler.postTask(() => console.log("usr-vis 2"), {
  priority: "user-visible",
});
scheduler.postTask(() => console.log("usr-blk 2"), {
  priority: "user-blocking",
});

// Task with default priority: user-visible
scheduler.postTask(() => {
  console.log("usr-vis 3 (default)");
});

The expected output is shown below: tasks are executed in priority order, and then declaration order.

usr-blk 1
usr-blk 2
usr-vis 1
usr-vis 2
usr-vis 3 (default)
bckg 1
bckg 2

Changing task priorities

Task priorities can also take their initial value from a TaskSignal passed to postTask() in the optional second argument. If set in this way, the priority of the task can then be changed using the controller associated with the signal.

Note: Setting and changing task priorities using a signal only works when the options.priority argument to postTask() is not set, and when the options.signal is a TaskSignal (and not an AbortSignal).

The code below first shows how to create a TaskController, setting the initial priority of its signal to user-blocking in the TaskController() constructor.

We then use addEventListener() to add an event listener to the controller's signal (we could alternatively use the TaskSignal.onprioritychange property to add an event handler). The event handler uses previousPriority on the event to get the original priority and TaskSignal.priority on the event target to get the new/current priority.

js

// Create a TaskController, setting its signal priority to 'user-blocking'
const controller = new TaskController({ priority: "user-blocking" });

// Listen for 'prioritychange' events on the controller's signal.
controller.signal.addEventListener("prioritychange", (event) => {
  const previousPriority = event.previousPriority;
  const newPriority = event.target.priority;
  console.log(`Priority changed from ${previousPriority} to ${newPriority}.`);
});

Finally, the task is posted, passing in the signal, and then we immediately change the priority to background by calling TaskController.setPriority() on the controller.

js

// Post task using the controller's signal.
// The signal priority sets the initial priority of the task
scheduler.postTask(() => console.log("Task 1"), { signal: controller.signal });

// Change the priority to 'background' using the controller
controller.setPriority("background");

The expected output is shown below. Note that in this case the priority is changed before the task is executed, but it could equally have been changed while the task was running.

js

// Expected output
// Priority changed from user-blocking to background.
// Task 1

Aborting tasks

Tasks can be aborted using either TaskController and AbortController, in exactly the same way. The only difference is that you must use TaskController if you also want to set the task priority.

The code below creates a controller and passes its signal to the task. The task is then immediately aborted. This causes the promise to be rejected with an AbortError, which is caught in the catch block and logged. Note that we could also have listened for the abort event fired on the TaskSignal or AbortSignal and logged the abort there.

js

// Declare a TaskController with default priority
const abortTaskController = new TaskController();
// Post task passing the controller's signal
scheduler
  .postTask(() => console.log("Task executing"), {
    signal: abortTaskController.signal,
  })
  .then((taskResult) => console.log(`${taskResult}`)) //This won't run!
  .catch((error) => console.error("Error:", error)); // Log the error

// Abort the task
abortTaskController.abort();

Delaying tasks

Tasks can be delayed by specifying an integer number of milliseconds in the options.delay parameter to postTask(). This effectively adds the task to the prioritized queue on a timeout, as might be created using setTimeout(). The delay is the minimum amount of time before the task is added to the scheduler; it may be longer.

The code below shows two tasks added (as arrow functions) with a delay.

js

// Post task as arrow function with delay of 2 seconds
scheduler
  .postTask(() => "Task delayed by 2000ms", { delay: 2000 })
  .then((taskResult) => console.log(`${taskResult}`));
scheduler
  .postTask(() => "Next task should complete in about 2000ms", { delay: 1 })
  .then((taskResult) => console.log(`${taskResult}`));

Specifications

Specification
Prioritized Task Scheduling
# dom-scheduler-posttask

Browser compatibility

BCD tables only load in the browser