[Medium] 📄 Event Loop
1. Зачем JavaScript нужна асинхронность? Объясните callback и event loop
Зачем JavaScript нужно асинхронное поведение? Объясните callback и event loop.
JavaScript по своей природе однопоточный. Одна из его основных задач — манипулирование DOM браузера. Если бы несколько потоков одновременно модифицировали один и тот же узел, поведение стало бы очень сложным. Чтобы этого избежать, JavaScript работает в однопоточной модели.
Асинхронное поведение — это практическое решение поверх этой модели. Если операция должна ждать 2 секунды, браузер не может просто замереть. Он выполняет синхронные задачи первыми, а асинхронные задачи ставятся в очередь задач (task queue).
Синхронный контекст выполнения можно представить как стек вызовов (call stack). Браузер сначала выполняет задачи в стеке вызовов. Когда стек вызовов становится пустым, он извлекает ожидающие задачи из очереди задач в стек вызовов.
- Браузер проверяет, пуст ли стек вызовов => Нет => продолжает выполнять задачи из стека вызовов.
- Браузер проверяет, пуст ли стек вызовов => Да => проверяет очередь задач => если задачи есть, перемещает одну в стек вызовов.
Этот повторяющийся цикл и есть концепция event loop.
console.log(1);
// Эта асинхронная функция является callback
setTimeout(function () {
console.log(2);
}, 0);
console.log(3);
// Порядок вывода: 1 3 2
2. Почему setInterval не точен по времени?
Почему
setIntervalне точен по времени?
-
JavaScript однопоточный (одна задача за раз, остальные ждут в очереди). Если callback
setIntervalвыполняется дольше самого интервала, следующий callback задерживается. Пример: интервал 1 секунда, но один callback занимает 2 секунды. Следующий запуск уже опаздывает на 1 секунду. Это смещение накапливается со временем. -
Браузеры и среды выполнения также устанавливают ограничения. В большинстве основных браузеров (Chrome, Firefox, Safari) практический минимальный интервал обычно составляет около 4 мс. Поэтому даже если вы установите 1 мс, реально он будет срабатывать примерно каждые 4 мс.
-
Высокая нагрузка на CPU или память может задерживать выполнение. Такие задачи, как обработка видео или изображений, часто вызывают задержки таймеров.
-
В JavaScript есть сборка мусора. Если callback интервала создаёт много объектов, работа GC может привносить дополнительные задержки.
Альтернатива (Alternative)
requestAnimationFrame
Если вы используете setInterval для анимации, рассмотрите замену на requestAnimationFrame.
- Синхронизация с перерисовкой: выполняется, когда браузер готов нарисовать следующий кадр. Это гораздо точнее, чем угадывание времени перерисовки с помощью
setIntervalилиsetTimeout. - Лучшая производительность: поскольку он синхронизирован с циклами перерисовки, он не будет выполняться, когда перерисовка не нужна (например, для фоновых вкладок), экономя вычисления.
- Автоматическое регулирование: подстраивает частоту выполнения под условия устройства, обычно около 60 FPS.
- Высокоточная метка времени: получает параметр
DOMHighResTimeStamp(микросекундная точность), полезный для точного тайминга анимации.
Пример
let startPos = 0;
function moveElement(timestamp) {
// обновляем позицию DOM-элемента
startPos += 5;
document.getElementById(
'myElement'
).style.transform = `translateX(${startPos}px)`;
// Продолжаем анимацию, пока элемент не достигнет целевой позиции
if (startPos < 500) {
requestAnimationFrame(moveElement);
}
}
// запуск анимации
requestAnimationFrame(moveElement);
moveElement() обновляет позицию каждый кадр (обычно 60 FPS), пока не достигнет 500 пикселей.
Обычно это даёт более плавную и естественную анимацию, чем setInterval.