← Назад к вопросам

Event Loop с задержками

3.0 Senior🔥 121 комментариев
#Node.js и JavaScript

Условие

Дан следующий код:

const myPromise = (delay) => new Promise((res) => setTimeout(res, delay));

setTimeout(() => console.log("setTimeout1"), 1000);
myPromise(1000).then(() => console.log("Promise1"));
setTimeout(() => console.log("setTimeout2"), 100);
myPromise(2000).then(() => console.log("Promise2"));
setTimeout(() => console.log("setTimeout3"), 2000);
myPromise(1000).then(() => console.log("Promise3"));
setTimeout(() => console.log("setTimeout4"), 1000);

Определите порядок вывода. Учитывайте реальные задержки таймеров.

Что проверяется

  • Понимание реального времени выполнения таймеров
  • Приоритет микрозадач при одновременном срабатывании
  • Работа с промисами и setTimeout

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Решение

Порядок вывода

setTimeout2          // 100 мс
setTimeout1          // 1000 мс
setTimeout4          // 1000 мс
Promise1             // 1000 мс (срабатывает после setTimeout1/4)
Promise3             // 1000 мс (срабатывает после setTimeout1/4)
setTimeout3          // 2000 мс
Promise2             // 2000 мс (срабатывает после setTimeout3)

Объяснение по временной шкале

t = 0 мс: Регистрация всех таймеров

const myPromise = (delay) => new Promise((res) => setTimeout(res, delay));

setTimeout(() => console.log("setTimeout1"), 1000);  // Срабатит в 1000мс
myPromise(1000).then(() => console.log("Promise1")); // Срабатит в 1000мс
setTimeout(() => console.log("setTimeout2"), 100);   // Срабатит в 100мс
myPromise(2000).then(() => console.log("Promise2")); // Срабатит в 2000мс
setTimeout(() => console.log("setTimeout3"), 2000);  // Срабатит в 2000мс
myPromise(1000).then(() => console.log("Promise3")); // Срабатит в 1000мс
setTimeout(() => console.log("setTimeout4"), 1000);  // Срабатит в 1000мс

Все таймеры добавлены в очередь, но они выполнятся в разное время.

t = 100 мс: Первый setTimeout

Macrotask Queue: setTimeout2 Event Loop: Выполняем setTimeout2 → Выводит: setTimeout2 Microtask Queue: Пусто

t = 1000 мс: Срабатывают сразу 4 события

В этот момент срабатывают:

  1. setTimeout для setTimeout1 (задержка 1000мс)
  2. setTimeout внутри myPromise(1000) для Promise1
  3. setTimeout для setTimeout4 (задержка 1000мс)
  4. setTimeout внутри myPromise(1000) для Promise3

Важно: setTimeout1 и setTimeout4 идут в очередь одновременно, так что порядок определяется регистрацией:

Макротаски (упорядочены по регистрации):
  1. callback для setTimeout1
  2. callback для setTimeout4

Event Loop выполняет:

  1. Первая макротаска (setTimeout1 callback) → Выводит: setTimeout1

    • Микротаски не добавлены
  2. Вторая макротаска (setTimeout4 callback) → Выводит: setTimeout4

    • Микротаски не добавлены

После выполнения всех макротасок проверяются микротаски:

  1. setTimeout из myPromise(1000) разрешил Promise1 → Выводит: Promise1
  2. setTimeout из myPromise(1000) разрешил Promise3 → Выводит: Promise3

t = 2000 мс: Срабатывают ещё 2 события

В этот момент срабатывают:

  1. setTimeout для setTimeout3 (задержка 2000мс)
  2. setTimeout внутри myPromise(2000) для Promise2

Event Loop выполняет:

  1. Макротаска (setTimeout3 callback) → Выводит: setTimeout3

  2. После выполнения макротаски проверяются микротаски:

    • setTimeout из myPromise(2000) разрешил Promise2 → Выводит: Promise2

Полная временная диаграмма

Время  Событие              Очередь макро    Очередь микро    Вывод
─────────────────────────────────────────────────────────────────────
0мс    Регистрация         setTimeout1      ∅                ∅
       setTimeout1(1000)    setTimeout2      ∅
       myPromise(1000)      setTimeout3      ∅
       setTimeout2(100)     setTimeout4      ∅
       myPromise(2000)      
       setTimeout3(2000)    
       myPromise(1000)      
       setTimeout4(1000)    

100мс  setTimeout2 готов    setTimeout1      ∅                setTimeout2
       Выполняем           setTimeout3      ∅
                           setTimeout4      

1000мс setTimeout1 готов    setTimeout1      Promise1(микро)  setTimeout1
       setTimeout4 готов    setTimeout4      Promise3(микро)
       myPromise1 готов     
       myPromise3 готов     
       
       Выполняем setTimeout1 setTimeout4      Promise1         
                                             Promise3         
       Выполняем setTimeout4                 Promise1
                                             Promise3
       
       Выполняем микротаски Promise1        ∅                Promise1
       Выполняем микротаски Promise3                         Promise3

2000мс setTimeout3 готов    ∅               Promise2(микро)   setTimeout3
       myPromise2 готов    
       
       Выполняем setTimeout3                Promise2
       
       Выполняем микротаски Promise2        ∅                Promise2

Ключевые правила

  1. Синхронный код: Выполняется первым (все setTimeout, Promise регистрируются)

  2. Порядок в очереди Macrotask: FIFO (первый пришёл — первый ушёл)

    • setTimeout1 зарегистрирован раньше, чем setTimeout4
    • В t=1000мс оба готовы, но setTimeout1 выполнится раньше
  3. После каждой Macrotask: Проверяются ВСЕ готовые микротаски

    • После setTimeout1 → выполняются Promise1 и Promise3 (если оба готовы)
    • После setTimeout3 → выполняется Promise2
  4. Задержка таймера: Минимальная, не гарантированная

    • setTimeout(0) выполнится в ~4мс минимум
    • Фактическое время зависит от CPU load

Почему Promise срабатывает ДЕ после setTimeout?

Потому что внутри myPromise есть setTimeout:

const myPromise = (delay) => 
  new Promise((res) => 
    setTimeout(res, delay)  // setTimeout вызывает resolve
  );

Поэтому Promise разрешается только когда внутренний setTimeout срабатывает. А resolve() тогда идёт в Microtask Queue.

Частые неправильные ответы

❌ "Promise выполнится в 1000мс, потом setTimeout1" ✓ Правильно: setTimeout1 выполнится в 1000мс, потом внутренний setTimeout разрешит Promise (микротаска)

❌ "Promise1 и Promise3 выполнятся в начале" ✓ Правильно: они дождутся разрешения своих setTimeout

❌ "setTimeout2 выполнится в конце" ✓ Правильно: setTimeout2 с задержкой 100мс выполнится ПЕРВЫМ

Event Loop с задержками | PrepBro