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

Что такое микрозадача?

1.0 Junior🔥 181 комментариев
#Браузер и сетевые технологии

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

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

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

Что такое микрозадача (Microtask)?

Микрозадача (microtask) — это задача, которая выполняется в JavaScript с более высоким приоритетом, чем обычные задачи (макрозадачи/macrotasks). Понимание микрозадач критически важно для овладения асинхронным JavaScript и event loop.

Event Loop — полная картина

JavaScript Event Loop имеет три очереди:

┌─────────────────┐
│  Call Stack     │ Выполняет синхронный код
└─────────────────┘
         ↓
┌─────────────────────────────────┐
│  Microtask Queue (приоритет 1)  │ ← Выполняется ПЕРВОЙ
│  - Promise.then/catch/finally   │
│  - async/await                  │
│  - queueMicrotask()             │
│  - MutationObserver             │
└─────────────────────────────────┘
         ↓
┌─────────────────────────────────┐
│  Macrotask Queue (приоритет 2)  │ ← Выполняется ПОСЛЕ microtask
│  - setTimeout/setInterval       │
│  - setImmediate                 │
│  - I/O операции                 │
│  - UI рендеринг                 │
└─────────────────────────────────┘

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

  1. Выполни все синхронные операции (Call Stack)
  2. Опусти Call Stack
  3. Выполни ВСЕ микрозадачи из очереди
  4. Выполни ОДНУ макрозадачу
  5. Вернись к шагу 3

Примеры микрозадач

Promise callbacks:

console.log('1');

Promise.resolve()
  .then(() => console.log('2'))
  .then(() => console.log('3'));

console.log('4');

// Вывод:
// 1
// 4
// 2
// 3

// Объяснение:
// 1. console.log('1') — синхронно выведет сразу
// 2. Promise.then() добавляется в микроочередь
// 3. console.log('4') — синхронно выведет сразу
// 4. Call Stack опустошен запускаем микротаски
// 5. Выполняется .then(() => console.log('2')), добавляется новый .then()
// 6. Выполняется .then(() => console.log('3'))

async/await (это Promise под капотом):

console.log('1');

async function myFunc() {
  console.log('2');
  await Promise.resolve();
  console.log('3');
}

myFunc();

console.log('4');

// Вывод:
// 1
// 2
// 4
// 3

// Объяснение:
// await эквивалентен .then(), добавляет задачу в микроочередь

Макрозадачи vs Микрозадачи

console.log('Синхронно 1');

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

Promise.resolve()
  .then(() => console.log('Микротаск (Promise)'));

console.log('Синхронно 2');

// Вывод:
// Синхронно 1
// Синхронно 2
// Микротаск (Promise)
// Макротаск (setTimeout)

// Даже с setTimeout 0, Promise выполнится РАНЬШЕ!

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

Опасный паттерн — надежда на порядок выполнения:

// Ненадежно
let data = null;

fetch('/api/data')
  .then(r => r.json())
  .then(result => { data = result; });

console.log(data); // null! Fetch еще не завершился

Правильно — использовать асинхронность правильно:

// Правильно
async function getData() {
  const response = await fetch('/api/data');
  const data = await response.json();
  return data;
}

getData().then(data => {
  console.log(data); // Теперь данные точно готовы
});

queueMicrotask() — явное добавление микрозадачи

console.log('Start');

queueMicrotask(() => {
  console.log('Microtask');
});

console.log('End');

// Вывод:
// Start
// End
// Microtask

// Это эквивалентно:
Promise.resolve().then(() => console.log('Microtask'));

Сложный пример с множественными очередями

console.log('Start');

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

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

console.log('End');

// Вывод:
// Start
// End
// Promise 1
// Promise 2
// setTimeout 1
// Promise inside setTimeout
// setTimeout inside Promise

Почему микрозадачи нужны?

Гарантия порядка — все микрозадачи выполняются до следующей макротаски:

// Promise.then() должен выполниться ДО setTimeout()
// Это критично для консистентности
Promise.resolve().then(() => {
  // Это выполнится перед любым setTimeout
});

setTimeout(() => {
  // Это выполнится после всех Promise
});

MutationObserver — микрозадача

const observer = new MutationObserver(() => {
  console.log('DOM изменился');
});

const element = document.getElementById('target');
observer.observe(element, { childList: true });

console.log('Синхронно');
element.innerHTML = '<p>New content</p>';
console.log('После изменения');

// Вывод:
// Синхронно
// После изменения
// DOM изменился (микротаск выполнится после синхронного кода)

Заключение

Микрозадачи — это фундаментальная часть JavaScript Event Loop. Понимание их порядка выполнения относительно макрозадач критически важно для написания предсказуемого асинхронного кода. Главное правило: все микрозадачи выполняются до следующей макротаски, что обеспечивает более предсказуемое и стабильное поведение асинхронного кода.