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

В какой структуре данных хранятся синхронные операции

1.8 Middle🔥 111 комментариев
#JavaScript Core

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

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

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

Структуры данных для синхронных операций в Event Loop

Что такое Event Loop?

Event Loop — это механизм в JavaScript, который обрабатывает синхронный и асинхронный код. Синхронные операции хранятся в специальных очередях (queues) и стеках (stack).

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

Call Stack — это основная структура для синхронного кода. Это стек (LIFO — Last In, First Out), где хранятся активные функции во время их выполнения.

function first() {
  console.log('first start');
  second();
  console.log('first end');
}

function second() {
  console.log('second');
  third();
}

function third() {
  console.log('third');
}

first();
// Порядок в Call Stack:
// 1. global -> first -> second -> third
// 2. global -> first (after third)
// 3. global (after first)

Визуализация стека:

Вызов first()      Вызов second()    Вызов third()
|  third |         |  third |        | |
|  second|         | second |        | |
|  first |         |  first |        | |
| global |         | global |        | global |
--------          --------          ----

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

Task Queue (Microtask Queue) — это очередь (FIFO — First In, First Out), в которой хранятся микротаски:

  • Promise callbacks (.then, .catch, .finally)
  • MutationObserver
  • queueMicrotask()
  • async/await (это Promise под капотом)
console.log('1: synchronous');

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

console.log('3: synchronous');

// Результат:
// 1: synchronous
// 3: synchronous
// 2: microtask

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

Callback Queue (Macrotask Queue) — это очередь (FIFO) для макротаск:

  • setTimeout / setInterval
  • setImmediate (Node.js)
  • requestAnimationFrame
  • I/O операции
  • UI события (click, scroll и т.д.)
console.log('1: start');

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

console.log('3: end');

// Результат:
// 1: start
// 3: end
// 2: setTimeout

Event Loop: Полный цикл

Вот как работает Event Loop пошагово:

console.log('1: Sync code');

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

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

console.log('4: Sync code');

// Порядок выполнения:
// 1. "1: Sync code"
// 2. "4: Sync code"
// 3. "3: Microtask (Promise)" <- Все микротаски
// 4. "2: Macrotask (setTimeout)" <- Макротаска

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

┌─────────────────────────────────────┐
│         JavaScript Engine           │
│  ┌──────────────────────────────┐   │
│  │      Call Stack              │   │  <- Выполняет синхронный код
│  │  [function1, function2, ...] │   │
│  └──────────────────────────────┘   │
└─────────────────────────────────────┘
           ↓
┌─────────────────────────────────────┐
│     Web APIs / Node.js APIs         │
│  (timers, fetch, events, etc.)      │
└─────────────────────────────────────┘
           ↓
┌─────────────────────────────────────┐
│      Microtask Queue                │
│  [Promise.then, queueMicrotask]     │  <- Выполняется после Call Stack
└─────────────────────────────────────┘
           ↓
┌─────────────────────────────────────┐
│      Callback Queue (Macrotask)     │
│  [setTimeout, setInterval, events]  │  <- После всех микротаск
└─────────────────────────────────────┘

4. Порядок выполнения (очень важно!)

function test() {
  // Уровень 1: Синхронный код (Call Stack)
  console.log('1');
  
  // Уровень 3: Микротаски (после Call Stack)
  Promise.resolve().then(() => console.log('2'));
  
  // Уровень 2: Макротаска (после всех микротаск)
  setTimeout(() => console.log('3'), 0);
  
  // Уровень 1: Синхронный код (Call Stack)
  console.log('4');
}

test();

// Результат:
// 1  <- Call Stack
// 4  <- Call Stack
// 2  <- Microtask Queue (Promise)
// 3  <- Callback Queue (setTimeout)

5. Более сложный пример

console.log('Start');

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

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

console.log('End');

// Порядок:
// Start        <- Синхронный код
// End          <- Синхронный код
// Promise 1    <- Микротаска (Promise)
// Promise 2    <- Микротаска (Promise.then)
// setTimeout 1 <- Макротаска
// Promise in setTimeout <- Микротаска ВНУТРИ макротаски
// setTimeout in Promise <- Макротаска (была в микротаске)

Структуры данных в разных частях Event Loop

ЧастьСтруктураПравилоПримеры
Call StackСтек (LIFO)Последний вызванный выполняется первымfunction calls
Microtask QueueОчередь (FIFO)Первый добавленный выполняется первымPromise, async/await
Callback QueueОчередь (FIFO)Первый добавленный выполняется первымsetTimeout, events

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

// Макротаска
setTimeout(() => {
  console.log('Macrotask 1');
  
  // Микротаска внутри макротаски
  Promise.resolve().then(() => console.log('Microtask inside Macrotask'));
  
  console.log('Still Macrotask 1');
}, 0);

// Микротаска
Promise.resolve()
  .then(() => {
    console.log('Microtask 1');
    // Макротаска внутри микротаски
    setTimeout(() => console.log('Macrotask inside Microtask'), 0);
  });

// Результат:
// Microtask 1
// Macrotask 1
// Still Macrotask 1
// Microtask inside Macrotask
// Macrotask inside Microtask

Ключевые правила

  1. Call Stack выполняет весь синхронный код первым
  2. Microtask Queue выполняется целиком, прежде чем начнётся следующая макротаска
  3. Callback Queue (Macrotask) выполняет одну макротаску, затем проверяет микротаски снова
  4. Нет гарантии для setTimeout(fn, 0) — это может быть даже медленнее 4мс из-за браузера

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

Если нужна максимальная производительность, используй микротаски (Promise):

// Быстрее
Promise.resolve().then(() => {
  console.log('выполнится раньше');
});

// Медленнее (минимум 4мс задержка в браузере)
setTimeout(() => {
  console.log('выполнится позже');
}, 0);
В какой структуре данных хранятся синхронные операции | PrepBro