Какие инструменты позволяют убрать нагрузку с основного потока в JavaScript
Что проверяют
Понимаете ли вы, что асинхронность и перенос работы с основного потока это разные вещи. Promise и async/await не делают тяжёлый расчёт фоновым, они откладывают продолжение, которое всё равно выполнится в том же потоке. Следом обычно спрашивают про цену передачи данных в воркер и про то, как дробить задачу, которую перенести нельзя.
Как отвечать
Настоящий перенос в другой поток:
- Web Worker: отдельный поток без доступа к DOM, общение через
postMessage. Данные копируются структурным клонированием, большие буферы лучше передавать как transferable (worker.postMessage(buf, [buf])), тогда копии нет. Обёртка Comlink превращает воркер в асинхронный объект; - OffscreenCanvas:
transferControlToOffscreen()отдаёт отрисовку canvas воркеру, полезно для графиков и карт; - WebAssembly в воркере: для вычислительных задач вроде обработки изображений;
- поток компоновщика: CSS-анимации
transformиopacityобычно идут мимо основного потока, в отличие от анимацииtopилиwidth; - Partytown и аналоги: сторонние скрипты аналитики переезжают в воркер.
Если работа обязана остаться в основном потоке, её дробят:
- уступка потока:
scheduler.yield()в Chromium, в остальных браузерах запасной путь черезsetTimeout,scheduler.postTask()с приоритетами,requestIdleCallbackдля фоновой мелочи; - React:
useTransitionиuseDeferredValueделают рендер прерываемым, виртуализация списков убирает лишнюю работу.
Привяжите ответ к метрике: задачи дольше 50 мс считаются длинными, а их последствия видны в INP, который входит в Core Web Vitals с 2024 года.
Где спотыкаются
Называют async/await и fetch способом разгрузить поток. Сам запрос не блокирует, но JSON.parse огромного ответа после него выполнится на основном потоке целиком.
Отправляют в воркер мелкие задачи. Накладные расходы на клонирование данных тогда больше выигрыша.
Считают requestAnimationFrame разгрузкой. Он лишь синхронизирует работу с кадром, и тяжёлый код внутри колбэка всё так же роняет частоту кадров.