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

Что такое Макрозадача в JavaScript?

1.2 Junior🔥 181 комментариев
#JavaScript Core

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

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

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

Что такое Макрозадача в JavaScript

Макрозадача (Macrotask, также известная как Task) — это единица работы в Event Loop браузера, которая имеет низкий приоритет и выполняется после всех микрозадач. Понимание макрозадач критично для предсказания порядка выполнения асинхронного кода.

Виды Макрозадач

Основные источники макрозадач:

  1. setTimeout / setInterval

    setTimeout(() => console.log('Макрозадача'), 0);
    
  2. setImmediate (Node.js, некоторые браузеры)

    setImmediate(() => console.log('Макрозадача'));
    
  3. requestAnimationFrame

    requestAnimationFrame(() => console.log('Макрозадача'));
    
  4. I/O операции

    • Чтение/запись файлов
    • Сетевые запросы
  5. User events

    • Клики мыши
    • Нажатия клавиш
    • Фокус элемента
  6. DOM events

    • load, unload
    • scroll, resize

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

Это главное отличие, которое нужно понимать:

console.log('1. Синхронный код');

Promise.resolve()
  .then(() => console.log('2. Микрозадача (Promise)'));

setTimeout(() => {
  console.log('3. Макрозадача (setTimeout)');
}, 0);

console.log('4. Синхронный код');

// Результат:
// 1. Синхронный код
// 4. Синхронный код
// 2. Микрозадача (Promise)
// 3. Макрозадача (setTimeout)

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

  • Event Loop сначала выполняет весь синхронный код
  • Затем обрабатывает ВСЕ микрозадачи (Promises, MutationObserver)
  • Затем обрабатывает ОДНУ макрозадачу (setTimeout)
  • Повторяет цикл

Event Loop с макрозадачами

┌─────────────────────────────┐
│   Синхронный код (Call Stack) │
└─────────────────────────────┘
              ↓
┌─────────────────────────────┐
│    Микрозадачи (Microtask)    │ ← Promise.then()
│                               │ ← MutationObserver
│                               │ ← queueMicrotask()
└─────────────────────────────┘
              ↓
┌─────────────────────────────┐
│    Макрозадачи (Macrotask)    │ ← setTimeout()
│                               │ ← setInterval()
│                               │ ← requestAnimationFrame()
└─────────────────────────────┘
              ↓
         Отрисовка (Rendering)
              ↓
         Повторить цикл

Сложный пример для понимания

console.log('1. Start');

// Макрозадача
setTimeout(() => {
  console.log('2. setTimeout 1');
  Promise.resolve().then(() => console.log('3. Promise в setTimeout'));
}, 0);

// Микрозадачи
Promise.resolve()
  .then(() => {
    console.log('4. Promise 1');
    setTimeout(() => console.log('5. setTimeout в Promise'), 0);
  })
  .then(() => console.log('6. Promise 2'));

// Макрозадача
setTimeout(() => {
  console.log('7. setTimeout 2');
}, 0);

console.log('8. End');

// Результат:
// 1. Start
// 8. End
// 4. Promise 1
// 6. Promise 2
// 2. setTimeout 1
// 3. Promise в setTimeout
// 7. setTimeout 2
// 5. setTimeout в Promise

Почему это важно

Проблема: Race conditions

let state = 'loading';

// Может выполниться в неправильном порядке!
setTimeout(() => {
  state = 'loaded';
}, 0);

Promise.resolve().then(() => {
  console.log(state); // 'loading', а не 'loaded'!
});

Решение: Используй микрозадачи для зависимостей

let state = 'loading';

Promise.resolve()
  .then(() => { state = 'loaded'; })
  .then(() => {
    console.log(state); // 'loaded' — правильно!
  });

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

Отложить работу с низким приоритетом:

function defer(fn: () => void) {
  setTimeout(fn, 0); // Макрозадача — низкий приоритет
}

// Vs

function deferMicro(fn: () => void) {
  Promise.resolve().then(fn); // Микрозадача — высокий приоритет
}

Батчинг обновлений:

let updates = [];

function scheduleUpdate(fn) {
  updates.push(fn);
  
  if (updates.length === 1) {
    // Выполнить все накопленные обновления в одной макрозадаче
    setTimeout(() => {
      const batch = updates;
      updates = [];
      batch.forEach(fn => fn());
    }, 0);
  }
}

Ключевые моменты

  • Макрозадача выполняется ПОСЛЕ всех микрозадач
  • setTimeout(fn, 0) — не означает «немедленно»
  • Между макрозадачами происходит отрисовка (rendering)
  • Микрозадачи имеют выше приоритет чем макрозадачи
  • requestAnimationFrame — макрозадача, но вызывается перед отрисовкой

Мастерство работы с Event Loop — залог написания предсказуемого асинхронного кода!