Scheduler: postTask() 方法

可用性有限

此特性不是基线特性,因为它在一些最广泛使用的浏览器中不起作用。

注意:此功能在 Web Workers 中可用。

Scheduler 接口的 postTask() 方法用于根据 优先级调度 任务。

该方法允许用户选择性地指定任务的最小延迟运行时间、任务优先级以及可用于修改任务优先级和/或中止任务的信号。它返回一个 Promise,该 Promise 会在任务回调函数执行完毕后解析(返回其结果),或在任务被中止或抛出错误时被拒绝。

任务优先级可以是 可变的或不可变的。如果任务优先级永远不需要更改,则应使用 options.priority 参数进行设置(此时通过信号设置的任何优先级都将被忽略)。您仍然可以将 AbortSignal(无优先级)或 TaskSignal 传递给 options.signal 参数来中止任务。

如果任务优先级可能需要更改,则不得设置 options.priority 参数。相反,应该创建一个 TaskController,并将其 TaskSignal 传递给 options.signal。任务优先级将从信号优先级初始化,之后可以使用信号关联的 TaskController 进行修改。

如果未设置优先级,则任务优先级默认为 "user-visible"

如果指定了延迟并且大于 0,则任务的执行将被延迟至少这么多毫秒。否则,任务将被立即调度进行优先级排序。

语法

js
postTask(callback)
postTask(callback, options)

参数

回调

一个实现任务的回调函数。回调函数的返回值用于解析此函数返回的 Promise。

options 可选

任务选项,包括:

priority 可选

任务的不可变 优先级。可能的值为:"user-blocking""user-visible""background"。如果设置了此参数,则在任务的整个生命周期内将使用此优先级,并且 signal 上设置的优先级将被忽略。

signal 可选

一个 TaskSignalAbortSignal,可用于中止任务(通过其关联的控制器)。

如果设置了 options.priority 参数,则任务优先级无法更改,并且将忽略信号上的任何优先级。否则,如果信号是 TaskSignal,则其优先级用于设置初始任务优先级,并且信号的控制器之后可以使用它来更改任务优先级。

delay 可选

任务将被添加到调度器队列的最小时间(以整数毫秒为单位)。实际延迟可能高于指定值,但不会低于。默认延迟为 0。

返回值

返回一个 Promise,该 Promise 会使用 callback 函数的返回值进行解析,或者可能使用 signal 的中止原因(AbortSignal.reason)进行拒绝。在回调执行期间抛出的错误也可能导致 Promise 被拒绝。

示例

以下示例是 Prioritized Task Scheduling API > 示例 中提供的实时示例的简化版本。

功能检测

通过在全局作用域(例如,窗口作用域中的 Window.scheduler 或工作线程作用域中的 WorkerGlobalScope.scheduler)中测试 scheduler 属性,来检查是否支持优先级任务调度。

例如,下面的代码如果在浏览器中支持该 API,则会输出 "Feature: Supported"。

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

基本用法

任务被发布,在第一个参数中指定回调函数(任务),在可选的第二个参数中指定任务优先级、信号和/或延迟。该方法返回一个 Promise,该 Promise 在回调函数执行完毕后解析,或因中止错误或函数中抛出的错误而被拒绝。

由于它返回一个 Promise,因此 postTask() 可以 与链式 Promise 一起使用。下面我们展示如何使用 then 等待 Promise 解析,或使用 catch 等待 Promise 被拒绝。未指定优先级,因此将使用默认优先级 user-visible

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

该方法也可以在 async function 中与 await 一起使用。下面的代码显示了如何使用此方法等待一个 user-blocking 任务。

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();

永久优先级

可以通过可选的第二个参数中的 priority 参数设置 任务优先级。以这种方式设置的优先级无法更改(是 不可变的)。

下面我们发布两组共三项任务,每组任务按优先级倒序排列。最后一项任务具有默认优先级。运行时,每项任务都会简单地记录其预期顺序(我们不需要等待结果,因为要显示执行顺序,无需等待)。

js
// three tasks, in reverse order of priority
scheduler.postTask(() => console.log("bkg 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("bkg 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)");
});

预期输出如下所示:任务按优先级顺序执行,然后按声明顺序执行。

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

更改任务优先级

任务优先级 也可以通过传递给 postTask() 的可选第二个参数中的 TaskSignal 来获取初始值。如果以此方式设置,则可以使用与信号关联的控制器 随后更改 任务优先级。

注意: 仅当 postTask()options.priority 参数未设置,并且 options.signalTaskSignal(而不是 AbortSignal)时,使用信号设置和更改任务优先级才有效。

下面的代码首先展示了如何创建 TaskController,并在其 TaskController() 构造函数 中将其信号的初始优先级设置为 user-blocking

然后,我们使用 addEventListener() 为控制器的信号添加事件监听器(我们也可以使用 TaskSignal.onprioritychange 属性来添加事件处理程序)。事件处理程序使用事件上的 previousPriority 来获取原始优先级,并使用事件目标上的 TaskSignal.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}.`);
});

最后,发布任务,传递信号,然后通过在控制器上调用 TaskController.setPriority() 将优先级立即更改为 background

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");

预期输出如下所示。请注意,在这种情况下,优先级在任务执行之前被更改,但它也可以在任务运行时被更改。

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

中止任务

可以使用 TaskControllerAbortController 以完全相同的方式中止任务。唯一的区别是,如果您还想设置任务优先级,则必须使用 TaskController

下面的代码创建一个控制器,并将其信号传递给任务。然后任务立即被中止。这会导致 Promise 被 AbortError 拒绝,该错误在 catch 块中被捕获并记录。请注意,我们也可以侦听 TaskSignalAbortSignal 上触发的 abort 事件,并在那里记录中止。

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();

延迟任务

可以通过在 postTask()options.delay 参数中指定一个整数毫秒数来延迟任务。这有效地将任务添加到优先级队列的超时中,就像使用 setTimeout() 创建的一样。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}`));

规范

规范
优先任务调度
# dom-scheduler-posttask

浏览器兼容性