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

В какой очередности выполняются микротаски и макротаски

1.7 Middle🔥 221 комментариев
#JavaScript Core

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

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

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

Очередность выполнения микротаск (Microtasks) и макротаск (Macrotasks)

Микротаски и макротаски — это концепция, которая лежит в основе асинхронности в JavaScript. Понимание их порядка выполнения критично для прогнозирования поведения асинхронного кода.

Что такое макротаски (Macrotasks)?

Макротаски — это обычные асинхронные операции, которые выполняются браузером в отдельной очереди. К ним относятся:

  • setTimeout
  • setInterval
  • setImmediate (в Node.js)
  • requestAnimationFrame
  • I/O операции
  • Рендеринг (отрисовка)
  • Обработка событий (click, input и т.д.)

Что такое микротаски (Microtasks)?

Микротаски — это более приоритетные асинхронные операции, которые выполняются ДО переходов к следующей макротаске. К ним относятся:

  • Promise.then(), Promise.catch(), Promise.finally()
  • async/await (это синтаксис сахара для Promise)
  • MutationObserver
  • queueMicrotask()

Очередность выполнения (Event Loop)

JavaScript использует Event Loop для управления выполнением кода. Вот порядок:

1. Выполни синхронный код
2. Выполни все микротаски (полностью очистить микротаску очередь)
3. Выполни одну макротаску
4. Выполни все микротаски (снова очистить полностью)
5. Повтори шаги 3-4

Пример для понимания

console.log('1. Синхронный код: НАЧАЛО');

setTimeout(() => {
  console.log('5. Макротаска: setTimeout');
}, 0);

Promise.resolve()
  .then(() => {
    console.log('3. Микротаска: Promise 1');
  })
  .then(() => {
    console.log('4. Микротаска: Promise 2');
  });

console.log('2. Синхронный код: КОНЕЦ');

/* Вывод:
1. Синхронный код: НАЧАЛО
2. Синхронный код: КОНЕЦ
3. Микротаска: Promise 1
4. Микротаска: Promise 2
5. Макротаска: setTimeout
*/

Почему микротаски выполняются раньше макротасок?

Browsers и Node.js обрабатывают микротаски с более высоким приоритетом:

console.log('1. Start');

setTimeout(() => {
  console.log('5. setTimeout');
  Promise.resolve().then(() => {
    console.log('6. Promise inside setTimeout');
  });
}, 0);

Promise.resolve()
  .then(() => {
    console.log('2. Promise 1');
    setTimeout(() => {
      console.log('7. setTimeout inside Promise');
    }, 0);
  })
  .then(() => {
    console.log('3. Promise 2');
  });

console.log('4. End');

/* Вывод:
1. Start
4. End
2. Promise 1
3. Promise 2
5. setTimeout
6. Promise inside setTimeout
7. setTimeout inside Promise
*/

Сложный пример с несколькими макротасками

console.log('1. Скрипт');

setTimeout(() => {
  console.log('4. setTimeout 1');
  Promise.resolve().then(() => {
    console.log('5. Promise в setTimeout 1');
  });
}, 0);

setTimeout(() => {
  console.log('7. setTimeout 2');
}, 0);

Promise.resolve()
  .then(() => {
    console.log('2. Promise 1');
    setTimeout(() => {
      console.log('8. setTimeout в Promise');
    }, 0);
  })
  .then(() => {
    console.log('3. Promise 2');
  });

console.log('6. Конец скрипта');

/* Вывод:
1. Скрипт
6. Конец скрипта
2. Promise 1
3. Promise 2
4. setTimeout 1
5. Promise в setTimeout 1
7. setTimeout 2
8. setTimeout в Promise
*/

Жизненный цикл Event Loop (подробный)

  1. Call Stack (стек вызовов) выполняет синхронный код
  2. Когда стек пуст:
    • Проверяется Microtask Queue
    • Все микротаски выполняются полностью
  3. Затем выполняется одна задача из Macrotask Queue
  4. Проверяется Microtask Queue снова
  5. Рендеринг (если нужен)
  6. Повторить с шага 2
┌─────────────────────────────────────────┐
│  Call Stack (синхронный код)            │
└─────────────────────────────────────────┘
           ↓
┌─────────────────────────────────────────┐
│  Microtask Queue (Promises, async/await) │
└─────────────────────────────────────────┘
           ↓
┌─────────────────────────────────────────┐
│  Macrotask Queue (setTimeout, events)    │
└─────────────────────────────────────────┘
           ↓
     Рендеринг
           ↓
        Повтор

async/await также использует микротаски

async function main() {
  console.log('1. Start');
  await Promise.resolve();
  console.log('2. After await'); // Это микротаска!
}

main();

setTimeout(() => {
  console.log('3. setTimeout');
}, 0);

/* Вывод:
1. Start
2. After await
3. setTimeout
*/

Практическое применение

Проблема: частая ошибка — ожидание того, что setTimeout выполнится раньше Promise:

// ❌ Ошибочное предположение
setTimeout(() => {
  console.log('Это выполнится раньше!');
}, 0);

Promise.resolve().then(() => {
  console.log('Это выполнится позже...');
});

// На самом деле Promise выполнится раньше!

Решение: помни приоритет:

  • Promises (микротаски) имеют приоритет выше
  • setTimeout (макротаски) выполняются после

Резюме

Порядок выполнения:

  1. Синхронный код (Call Stack)
  2. Все микротаски полностью (Promises, async/await)
  3. Одна макротаска (setTimeout, события)
  4. Все микротаски снова
  5. Рендеринг
  6. Повторить с пункта 3

Запомни: Микротаски выполняются РАНЬШЕ макротасок. Это основное правило Event Loop в JavaScript.

В какой очередности выполняются микротаски и макротаски | PrepBro