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

В какой буфер попадает callback

2.0 Middle🔥 201 комментариев
#JavaScript Core#Браузер и сетевые технологии

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

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

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

В какой буфер попадает callback

Этот вопрос относится к системе управления асинхронными операциями в JavaScript — Event Loop и различные очереди обработки колбэков. Понимание этого механизма критично для написания предсказуемого кода.

Event Loop и очереди

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

1. Call Stack (Стек вызовов)

Основной стек, где выполняется синхронный код:

function first() {
  second();
  console.log("First");
}

function second() {
  console.log("Second");
}

first();
// Вывод: Second, First

2. Microtask Queue (Очередь микротасков)

Это первая асинхронная очередь, которая обрабатывается после выполнения текущего стека, но до Macrotask Queue.

В Microtask Queue попадают:

  • Promise.then() / Promise.catch() / Promise.finally()
  • async/await (это синтаксический сахар над Promises)
  • queueMicrotask() — явное добавление в очередь
  • MutationObserver — отслеживание изменений DOM
console.log("Start");

Promise.resolve()
  .then(() => console.log("Promise 1"))
  .then(() => console.log("Promise 2"));

console.log("End");

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

3. Macrotask Queue (Очередь макротасков)

Это вторая асинхронная очередь, которая обрабатывается после полной очистки Microtask Queue.

В Macrotask Queue попадают:

  • setTimeout() / setInterval()
  • setImmediate() (Node.js)
  • requestAnimationFrame() (браузер)
  • I/O операции (чтение файлов, сетевые запросы)
  • UI рендеринг
  • Event handlers (клики, события)
console.log("Start");

setTimeout(() => {
  console.log("Timeout");
}, 0);

Promise.resolve()
  .then(() => console.log("Promise"));

console.log("End");

// Вывод:
// Start
// End
// Promise
// Timeout

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

console.log("Script start");

setTimeout(() => {
  console.log("setTimeout 1");
}, 0);

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

console.log("Script end");

// Вывод:
// Script start
// Script end
// Promise 1
// Promise 2
// setTimeout 1
// setTimeout 2

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

  1. Синхронный код (Call Stack) выполняется первым
  2. Microtask Queue полностью очищается (все Promises)
  3. UI рендеринг (если необходимо)
  4. Одна задача из Macrotask Queue выполняется
  5. Переход к шагу 2

requestAnimationFrame специальный случай

requestAnimationFrame — это гибрид между Microtask и Macrotask:

console.log("Start");

requestAnimationFrame(() => console.log("rAF"));

Promise.resolve()
  .then(() => console.log("Promise"));

setTimeout(() => console.log("Timeout"), 0);

console.log("End");

// Вывод:
// Start
// End
// Promise
// rAF
// Timeout

Практический пример из React

function MyComponent() {
  const handleClick = () => {
    console.log("1. Click handler");
    
    setTimeout(() => console.log("4. Timeout"), 0);
    
    Promise.resolve()
      .then(() => console.log("3. Promise"));
    
    console.log("2. Sync code");
  };
  
  return <button onClick={handleClick}>Click me</button>;
}

// При клике на кнопку:
// 1. Click handler
// 2. Sync code
// 3. Promise
// 4. Timeout

Лучшие практики

  1. Используй async/await вместо .then() для читаемости
  2. Избегай глубокой вложенности обещаний
  3. Понимай порядок выполнения при работе с таймерами и Promises
  4. Используй queueMicrotask() только когда это действительно необходимо
  5. Профилируй производительность для операций в Event Loop

Знание этого механизма помогает избежать race conditions и неожиданного поведения асинхронного кода.