Работа с JavaScript Scheduler API

Источник: «Working with JavaScript's Scheduler API»
Откройте для себя возможности управления приоритетами и контроля выполнения задач в JavaScript с помощью нового Scheduler API. Новый подход поможет вам стать более продуктивными и эффективными в своей работе.

JavaScript Scheduler API представляет стандартизированный подход к управлению приоритетами задач в веб-приложениях.

Разработчики JavaScript исторически полагались на setTimeout(0) для передачи главного потока. Новый API обеспечивает более точный контроль над тем, как и когда выполняются задачи.

Традиционному подходу setTimeout к передаче главного потока не хватает детального контроля и правильной расстановки приоритетов. API Scheduler устраняет эти недостатки, предлагая более надёжное решение.

// Старый подход с setTimeout
setTimeout(() => {
processNextBatch();
}, 0);

Scheduler API предлагает два основных метода для лучшего контроля: yield() и postTask(). Более простой метод yield() обеспечивает чистый способ приостановить выполнение.

// Новый подход с scheduler.yield()
await scheduler.yield();
processNextBatch();

Для более сложных сценариев функция postTask() позволяет планировать задачи на основе приоритетов:

await scheduler.postTask(() => {
processNextBatch() },
{ priority: 'background' }
);

Уровни приоритета задач

API планировщика поддерживает три уровня приоритета для задач: user-blocking, user-visible и background. Задачи выполняются в порядке приоритета, то есть в той последовательности, в которой они были добавлены в очередь планировщика.

// TypeScript
enum TaskPriority {
"user-blocking",
"user-visible",
"background"
};

Уровень приоритета определяет, когда и насколько срочно браузер должен выполнить задачу. Вот что означает каждый из приоритетов:

Управление несколькими задачами с приоритетами

Scheduler API отлично справляется с управлением несколькими задачами с разными приоритетами, обеспечивая выполнение критически важных операций в первую очередь и откладывая менее важную работу. Когда несколько задач выполняются одновременно, браузер сначала выполняет их в порядке приоритета, а затем в порядке очереди.

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

Динамическое изменение приоритета

Задачи могут менять приоритет во время выполнения используя TaskController. Это ценно, когда важность задачи меняется в зависимости от взаимодействия с пользователем или состояния системы.

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 () => {
// Запуск задачи как `background`
await processInitialData();

// Повышение приоритета при необходимости
controller.setPriority('user-visible');
await processCriticalData();
},
{ signal: controller.signal }
);

Прерывание задач

Scheduler API позволяет прерывать задачи как через TaskController, так и через AbortController

// Базовое прерывание задачи
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');
}
});

// Прерывание задачи
controller.abort();

Поддержка браузерами и резервный паттерн

Прежде чем использовать Scheduler API, проверьте, поддерживает ли его браузер. Создайте обёртку, при необходимости возвращающуюся к setTimeout.

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

/*
Используйте этот запасной шаблон, чтобы обеспечить работу приложения
во всех браузерах, используя преимущества Scheduler API, где это возможно.
*/


const taskScheduler = createScheduler();

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

Scheduler API продолжает эволюционировать, а метод yield() момент написания статьи () помечен как экспериментальный. Поддержка браузеров постоянно развивается, и в основных браузерах реализована ключевая функциональность. Дизайн API позволяет расширять его в будущем, сохраняя обратную совместимость.

Этот стандартизированный подход к планированию задач представляет значительный шаг вперёд для производительности веб-приложений. Он позволяет создавать более отзывчивые интерфейсы и лучше использовать ресурсы, не прибегая к специфичным для конкретной реализации хакам или обходным путям.

Scheduler API решает фундаментальные проблемы производительности веб-приложений. Его чёткая модель приоритетов и возможность прерывания обеспечивают инструменты, необходимые для создания более отзывчивых приложений. По мере расширения поддержки браузеров этот API станет неотъемлемой частью набора инструментов веб-разработчика.

Комментарии


Дополнительные материалы

Предыдущая Статья

CSS content-visibility: Улучшение производительности веб-сайтов

Следующая Статья

HTML элемент search