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

Event Loop с Promise.reject

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

Условие

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

console.log(1);
setTimeout(() => console.log(2));
Promise.reject(3).catch(console.log);
new Promise(resolve => setTimeout(resolve)).then(() => console.log(4));
Promise.resolve(5).then(console.log);
console.log(6);
setTimeout(() => console.log(7), 0);

Определите порядок вывода и объясните почему.

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

  • Понимание Event Loop
  • Работа с Promise.reject и catch
  • Макрозадачи, порождающие микрозадачи
  • Порядок выполнения setTimeout с нулевой задержкой

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

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

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

Решение

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

1
6
3
5
2
4
7

Объяснение

Это типичная задача на понимание Event Loop в JavaScript. Разберу пошагово.

Основные концепции

Call Stack — стек синхронного кода

Microtask Queue — очередь микрозадач (Promises, queueMicrotask, MutationObserver)

Macrotask Queue — очередь макрозадач (setTimeout, setInterval, setImmediate, I/O)

Порядок исполнения

  1. Synchronous code (Call Stack):

    console.log(1);      // Выводит: 1
    setTimeout(...);      // Добавляет в Macrotask Queue
    Promise.reject(3)..  // Добавляет в Microtask Queue
    new Promise(...);    // setTimeout создаёт новый Promise
    Promise.resolve(5).. // Добавляет в Microtask Queue
    console.log(6);      // Выводит: 6
    setTimeout(...);      // Добавляет в Macrotask Queue
    

    Вывод: 1, 6

  2. Microtask Queue (после синхронного кода):

    • Promise.reject(3).catch(console.log) → Выводит: 3
    • Promise.resolve(5).then(console.log) → Выводит: 5
  3. Macrotask Queue (после всех микрозадач):

    • Первый setTimeout → Выводит: 2
     - Внутри: new Promise с setTimeout
     - Добавляет новую макрозадачу в очередь
  • Второй setTimeout → Выводит: 7
  1. Микрозадачи из новой Promise (после первого setTimeout):
    • .then(() => console.log(4)) → Выводит: 4

Визуализация Event Loop

┌─────────────────────────────────────────────────────┐
│ Шаг 1: Синхронный код (Call Stack)                 │
│ console.log(1)         → 1                          │
│ console.log(6)         → 6                          │
│ Добавлены в очереди:                               │
│ - Microtask: Promise.reject catch, Promise.resolve │
│ - Macrotask: 2 setTimeout                           │
└─────────────────────────────────────────────────────┘
         ↓
┌─────────────────────────────────────────────────────┐
│ Шаг 2: Microtask Queue                              │
│ Promise.reject(3).catch(console.log) → 3            │
│ Promise.resolve(5).then(console.log) → 5            │
└─────────────────────────────────────────────────────┘
         ↓
┌─────────────────────────────────────────────────────┐
│ Шаг 3: Первая Macrotask - setTimeout (2)            │
│ console.log(2)         → 2                          │
│ Добавлена новая Promise с setTimeout               │
└─────────────────────────────────────────────────────┘
         ↓
┌─────────────────────────────────────────────────────┐
│ Шаг 4: Вторая Macrotask - setTimeout (7)            │
│ console.log(7)         → 7                          │
└─────────────────────────────────────────────────────┘
         ↓
┌─────────────────────────────────────────────────────┐
│ Шаг 5: Microtask из новой Promise                   │
│ .then(() => console.log(4)) → 4                     │
└─────────────────────────────────────────────────────┘

Разбор каждой строки

console.log(1);  // Выводит 1 сразу
setTimeout(() => console.log(2));  // Идёт в Macrotask Queue
Promise.reject(3).catch(console.log);  // Идёт в Microtask Queue
new Promise(resolve => setTimeout(resolve))
  .then(() => console.log(4));
// setTimeout внутри Promise идёт в Macrotask Queue
// .then() идёт в Microtask Queue, но выполнится после resolve
Promise.resolve(5).then(console.log);  // Идёт в Microtask Queue
console.log(6);  // Выводит 6 сразу
setTimeout(() => console.log(7), 0);  // Идёт в Macrotask Queue

Почему именно такой порядок?

Event Loop правило:

  1. Выполняй весь синхронный код (Call Stack)
  2. Выполняй ВСЕ микрозадачи (Microtask Queue)
  3. Выполняй одну макрозадачу (Macrotask Queue)
  4. Повтори шаги 2-3

Ключевой момент: После каждой макрозадачи проверяются ВСЕ микрозадачи!

Альтернативный пример для закрепления

console.log('a');
setTimeout(() => {
  console.log('b');
  Promise.resolve().then(() => console.log('c'));
}, 0);
Promise.resolve().then(() => console.log('d'));
console.log('e');

Вывод: a, e, d, b, c

Почему так?

  • Синхронно: a, e
  • Микротаски: d
  • Первая макротаска (setTimeout): b
  • Микротаски из макротаски: c

Частые ошибки

Неправильно: думать, что Promise.reject срабатывает после всех макротасок ✓ Правильно: микрозадачи (Promise) выполняются ДО следующей макротаски

Неправильно: думать, что setTimeout(0) выполнится сразу ✓ Правильно: setTimeout(0) — минимальная задержка, идёт в очередь макротасок

Для запоминания

Мнемоника: Синхронное → Все микрозадачи → Одна макрозадача → Повторить

Примеры очередей:

  • Microtask: Promise.then, Promise.catch, queueMicrotask, MutationObserver
  • Macrotask: setTimeout, setInterval, setImmediate, I/O, UI rendering