Как положить задачи в очередь чтобы не блокировать event loop: использовать микротаски или макротаски
Что проверяют
Понимаете ли вы, что очередь микротасков выбирается целиком до следующей отрисовки и поэтому от зависания не спасает. Правильный ответ на вопрос из заголовка: чтобы отдать браузеру управление, нужны макротаски. Следом обычно просят объяснить порядок вывода в коде с setTimeout и Promise.then.
Как отвечать
Начните с устройства цикла: браузер берёт одну задачу из очереди макротасков, выполняет её, затем опустошает всю очередь микротасков, включая добавленные по ходу, и только после этого может перерисовать страницу. Значит, всё, что стоит в микротасках, для пользователя выглядит как один длинный синхронный кусок.
- микротаски (
Promise.then,queueMicrotask,MutationObserver): чтобы выполнить код сразу после текущего синхронного, до событий и рендера. Подходят для согласованности состояния и батчинга, но не для дробления тяжёлой работы; - макротаски (
setTimeout,MessageChannel, события): между ними браузер успевает обработать ввод и отрисовать кадр. Большой цикл режут на порции по несколько миллисекунд и каждую следующую ставят новой задачей; scheduler.postTask()иscheduler.yield(): современный способ уступить поток с приоритетом, поддерживается пока не везде, нужен фолбэк;requestAnimationFrame: для изменений, которые должны попасть в ближайший кадр, аrequestIdleCallbackдля фоновой работы;- Web Worker: если вычисление тяжёлое само по себе, его выносят из главного потока, дробление тут лишь смягчает проблему.
Ориентир: задача длиннее 50 мс считается long task и ухудшает INP.
Где спотыкаются
Говорят, что микротаски «быстрее, значит лучше для отзывчивости». На деле рекурсивная цепочка промисов вешает вкладку так же, как синхронный бесконечный цикл: до рендера очередь просто не доходит.
Считают setTimeout(fn, 0) мгновенным. После пяти вложенных вызовов браузер поднимает задержку до 4 мс, а в фоновой вкладке таймеры троттлятся до раза в секунду и реже. Поэтому планировщик React уступает поток через MessageChannel.
Дробят работу, но порции делают по 200 мс. Формально event loop свободен, а интерфейс всё равно дёргается: каждый кусок должен укладываться в несколько миллисекунд.