Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Цикл событий

Некоторые действия занимают время: ожидание таймера, ответ сервера, загрузка файла. JavaScript не стоит на месте, пока они идут, а продолжает работу и возвращается к результату позже. Чтобы писать такой код без сюрпризов, нужно понимать, в каком порядке он выполняется.

Синхронный код

Обычный код называют синхронным: инструкции выполняются строго по порядку, следующая начинается только после окончания предыдущей:

console.log('Один');
console.log('Два');
console.log('Три');
// Один
// Два
// Три

JavaScript однопоточный: в каждый момент он выполняет только одну инструкцию. Две функции не могут работать одновременно.

Асинхронный код

Асинхронный код — это код, который запускается сейчас, а заканчивается позже. Функция setTimeout() откладывает вызов другой функции на заданное число миллисекунд:

setTimeout(() => {
  console.log('Прошло 300 мс');
}, 300);
// Прошло 300 мс

Пока таймер ждёт, JavaScript свободен и может выполнять другой код.

Очередь задач

Когда таймер срабатывает, его функция не выполняется сразу, а встаёт в очередь задач. JavaScript берёт задачи из очереди, только когда закончит текущий код:

console.log('Начало');
 
setTimeout(() => {
  console.log('Таймер');
}, 0);
 
console.log('Конец');
// Начало
// Конец
// Таймер

Даже с нулевой задержкой таймер выполнился последним: сначала закончился весь основной код.

Цикл событий

Механизм, который снова и снова проверяет очередь и запускает следующую задачу, называется циклом событий (event loop). Его работу можно описать так:

ШагЧто происходит
1Выполняется весь текущий синхронный код
2Выполняются все микрозадачи — обработчики промисов
3Из очереди берётся одна задача, например функция таймера
4Снова шаг 2 — и так, пока есть задачи

Микрозадачи

Обработчики промисов (о них — в следующих уроках) попадают в отдельную очередь микрозадач. Она выполняется раньше обычных задач:

setTimeout(() => console.log('Таймер'), 0);
 
Promise.resolve().then(() => console.log('Промис'));
 
console.log('Обычный код');
// Обычный код
// Промис
// Таймер

Долгий код всё задерживает

Пока выполняется синхронный код, ни одна задача из очереди не запустится. Этот цикл занимает JavaScript на 200 мс, и таймер ждёт его окончания:

const start = Date.now();
 
setTimeout(() => {
  const waited = Date.now() - start;
  console.log(waited >= 200); // true
}, 0);
 
while (Date.now() - start < 200) {
  // просто ждём
}

В браузере на это время замирает вся страница: кнопки не нажимаются, анимация останавливается.

Частая ошибка

Ожидать, что setTimeout(..., 0) выполнится немедленно. Задержка — это лишь минимальное время ожидания: функция запустится не раньше, чем закончится текущий код и все микрозадачи. Если результат нужен сразу, не откладывайте код в таймер, а если нужен порядок «сначала одно, потом другое» — используйте промисы и await.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.