Планировщик: метод yield()
Ограниченная доступность
Эта функция не относится к Baseline, так как не работает в некоторых из наиболее широко используемых браузеров.
Примечание: Эта функция доступна в Web Workers.
Экспериментальная функция: Это экспериментальная технология
Перед использованием в продакшене внимательно проверьте таблицу совместимости с браузерами.
Метод yield() интерфейса Scheduler используется для уступки управления главному потоку во время выполнения задачи и продолжения выполнения позже, с запланированным продолжением как задачей с приоритетом (см. API планирования задач с приоритетом для получения дополнительной информации). Это позволяет разбивать длительные операции, чтобы браузер оставался отзывчивым.
Задача может быть продолжена, когда промис, возвращаемый методом, будет выполнен. Приоритет выполнения промиса по умолчанию задается "user-visible", но может быть изменён, если вызов yield() происходит внутри обратного вызова Scheduler.postTask().
Кроме того, продолжение работы после вызова yield() может быть отменено, если оно происходит внутри обратного вызова postTask() и задача прерывается.
Синтаксис
yield()
Параметры
Нет.
Возвращаемое значение
Возвращает Promise, который выполнен с undefined, или отклонен с AbortSignal.reason.
Примеры
Проверка наличия функции
Проверьте поддержку планирования задач с приоритетом, проверив наличие scheduler.yield в globalThis, как в окне, так и в области работника.
Например, код ниже выведет "scheduler.yield: Supported" если API поддерживается в текущем браузере.
// Check for support before using.
if (globalThis.scheduler?.yield) {
console.log("scheduler.yield: Supported");
} else {
console.error("scheduler.yield: NOT Supported");
}
Основное использование
Длительные задачи могут быть разделены путём ожидания scheduler.yield(). Функция возвращает промис, уступая управление главному потоку, что позволяет браузеру выполнять другие ожидающие задачи, например, реагировать на пользовательский ввод, при необходимости. Браузер планирует последующую задачу, которая выполняет промис, после чего выполнение кода может продолжиться с того места, где оно остановилось.
Например, если обработчик события click на кнопке приводит к значительной работе по загрузке и отображению нового содержимого страницы, никакой визуальной обратной связи для пользователя о том, что его щелчок по кнопке даже был зарегистрирован страницей, не будет до тех пор, пока эта работа не завершится. В обработчик событий можно вставить scheduler.yield() для отображения быстрого отклика (например, вращающегося индикатора), а затем остальную работу можно выполнить, когда выполнение возобновится после yield.
button.addEventListener("click", async () => {
// Provide immediate feedback so the user knows their click was received.
showSpinner();
await scheduler.yield();
// Do longer processing
doSlowContentSwap();
});
Также может быть достаточно обеспечить быструю обратную связь с пользовательским интерфейсом по умолчанию. Например, если обработчик события change на флажке запускает медленную фильтрацию содержимого страницы, можно вставить вызов scheduler.yield() для немедленного отображения изменения состояния флажка перед продолжением остальной части ответа на событие.
checkbox.addEventListener("change", async () => {
await scheduler.yield();
doSlowContentFiltering();
});
В ситуациях, когда необходимо выполнить длительную работу в основном потоке, которая может быть разбита на серию задач, scheduler.yield() можно вызывать многократно, чтобы страница оставалась отзывчивой на протяжении всего процесса.
function doWork(value) {
console.log(`work chunk ${value}`);
}
const workList = [0, 1, 2, 3, 4];
for (const work of workList) {
doWork(work);
await scheduler.yield();
}
Приоритет yield
Порядок выполнения промиса, возвращаемого scheduler.yield(), по отношению к другим задачам определяется неявным приоритетом задачи.
По умолчанию, scheduler.yield() выполняется с приоритетом "user-visible". Однако продолжение работы после вызова scheduler.yield() имеет немного другое поведение, чем задачи scheduler.postTask() с таким же priority.
scheduler.yield() помещает свою задачу в очередь с повышенным приоритетом по сравнению с scheduler.postTask() с таким же уровнем приоритета. Таким образом, например, продолжение scheduler.yield() с приоритетом "user-visible" будет иметь приоритет после задач scheduler.postTask() более высокого уровня приоритета "user-blocking", но перед задачами scheduler.postTask() с таким же "user-visible" приоритетом (в спецификации это определяется эффективным приоритетом очереди задач).
Иногда это описывается как scheduler.yield() помещает свою задачу в начало очереди уровня приоритета, а scheduler.postTask() задачи помещаются в конец. Это может быть полезной ментальной моделью. В ситуациях с небольшим количеством задач это означает, что с одинаковым приоритетом продолжение scheduler.yield() будет выполняться первой, предоставляя дополнительную гибкость в планировании задач. Например:
scheduler.postTask(() => console.log("user-visible postTask"));
scheduler.postTask(() => console.log("user-blocking postTask"), {
priority: "user-blocking",
});
await scheduler.yield();
console.log("user-visible yield");
выведет следующее:
user-blocking postTask user-visible yield user-visible postTask
Однако в случаях с несколькими вызовами scheduler.yield(), важность того, что задачи продолжения scheduler.yield() помещаются в очередь с повышенным приоритетом, становится важной, потому что вторая задача scheduler.yield() не будет выполнена раньше уже имеющейся в очереди.
Если одна функция отдает свою работу до того, как это сделает вторая функция, первая функция, которая отдала управление, продолжится первой. Например:
async function first() {
console.log("starting first function");
await scheduler.yield();
console.log("ending first function");
}
async function second() {
console.log("starting second function");
await scheduler.yield();
console.log("ending second function");
}
first();
second();
выведет следующее:
starting first function starting second function ending first function ending second function
Наследование приоритетов задач
Вызов scheduler.yield() внутри задачи scheduler.postTask() наследует приоритет задачи. Например, работа после scheduler.yield() внутри задачи низкого приоритета "background" также будет запланирована как "background" по умолчанию (но, опять же, помещена в очередь с повышенным приоритетом "background" , так что выполнение будет происходить перед любыми "background" postTask() задачами).
Например:
async function backgroundWork() {
scheduler.postTask(() => console.log("background postTask"), {
priority: "background",
});
scheduler.postTask(() => console.log("user-visible postTask"), {
priority: "user-visible",
});
// yield() inherits "background" priority from surrounding task.
await scheduler.yield();
console.log("default-background yield");
}
await scheduler.postTask(backgroundWork, { priority: "background" });
выведет следующее:
user-visible postTask default-background yield background postTask
Продолжения scheduler.yield() будут наследоваться от приоритета содержащей scheduler.postTask() задачи, включая динамически изменённый приоритет задачи.
Прерывание yield
Аналогично настройке приоритета, вызов scheduler.yield() напрямую прервать нельзя, но он унаследует сигнал прерывания от окружающей задачи scheduler.postTask(). Прерывание задачи также прервёт любые ожидающие yield внутри неё.
В этом примере используется TaskController для прерывания задачи с scheduler.yield() внутри неё.
const taskController = new TaskController();
function firstHalfOfWork() {
console.log("first half of work");
taskController.abort("cancel work");
}
function secondHalfOfWork() {
// Never runs.
console.log("second half of work");
}
scheduler.postTask(
async () => {
firstHalfOfWork();
await scheduler.yield();
secondHalfOfWork();
},
{ signal: taskController.signal },
);
Пример несколько искусственный, так как всегда вызывает taskController.abort() вызов внутри самой задачи, но вызов abort() может исходить откуда угодно. Например, он может быть вызван нажатием пользователем кнопки «Отмена».
В этом случае abort() происходит после того, как задача scheduler.postTask() уже началась ("first half of work" выводится), но вызов yield наследует сигнал прерывания, поэтому вызов await scheduler.yield() выбросит исключение с причиной прерывания "cancel work".
Использование yield() внутри requestIdleCallback()
Вызовы scheduler.yield() также наследуют приоритет от Window.requestIdleCallback(), если они вызваны внутри функции обратного вызова. В этом случае значение приоритета "background" наследуется. Однако обратите внимание, что вызовы scheduler.yield() внутри обратных вызовов requestIdleCallback() не могут быть прерваны.
Спецификации
Совместимость с браузерами
| Рабочие столы | Мобильные устройства | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| Chrome | Edge | Firefox | Opera | Safari | Chrome Android | Firefox для Android | Opera Android | Safari на iOS | Samsung Internet | WebView Android | |
yield |
129 | 129 | Нет | 115 | Нет | 129 | Нет | 86 | Нет | Нет | 129 |
См. также
© 2005–2024 MDN contributors.
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.
https://developer.mozilla.org/en-US/docs/Web/API/Scheduler/yield