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

Как задачи проходят путь до вызова в Event Loop?

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

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

🐱
deepseek-v3.2PrepBro AI4 апр. 2026 г.(ред.)

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

Механизм обработки задач в Event Loop

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

Основные компоненты системы

Система обработки состоит из трех ключевых элементов:

  • Стек вызовов (Call Stack) — выполняет синхронный код последовательно
  • Очереди задач (Task Queues) — хранят асинхронные задачи разных типов
  • Сам Event Loop — координирует перемещение задач между очередями и стеком

Типы очередей и их приоритеты

В современных движках JavaScript существует несколько типов очередей:

1. Очередь макрозадач (Task Queue/Macrotask Queue)

// Примеры макрозадач:
setTimeout(() => console.log('setTimeout'), 0);
setInterval(() => console.log('setInterval'), 1000);
// I/O операции, события DOM, сетевые запросы

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

// Примеры микрозадач:
Promise.resolve().then(() => console.log('Promise'));
queueMicrotask(() => console.log('queueMicrotask'));
// MutationObserver, process.nextTick (в Node.js)

3. Очередь анимаций (Animation Frames)

// requestAnimationFrame
requestAnimationFrame(() => console.log('RAF callback'));

Детальный путь задачи через Event Loop

Этап 1: Инициализация задачи

Когда возникает асинхронная операция:

console.log('Старт');

setTimeout(() => {
    console.log('Таймаут выполнен');
}, 0);

Promise.resolve()
    .then(() => console.log('Промис выполнен'));

console.log('Конец');

Порядок вывода:

  1. Старт
  2. Конец
  3. Промис выполнен
  4. Таймаут выполнен

Этап 2: Помещение в соответствующую очередь

  • Макрозадачи попадают в очередь макрозадач через Web APIs
  • Микрозадачи попадают в очередь микрозадач
  • Callback из requestAnimationFrame попадает в очередь анимаций

Этап 3: Работа Event Loop — алгоритм одного цикла

  1. Выполнение синхронного кода из стека вызовов
  2. Обработка микрозадач — Event Loop выполняет ВСЕ задачи из очереди микрозадач до её полного опустошения
  3. Рендеринг (при необходимости) — обновление DOM, выполнение анимаций
  4. Выбор одной макрозадачи из очереди макрозадач
  5. Повторение цикла
// Наглядный пример приоритетов
setTimeout(() => console.log('Макрозадача 1'), 0);

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

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

// Результат:
// Микрозадача 1
// Микрозадача 3
// Микрозадача 2
// Макрозадача 1

Критические аспекты обработки

Блокировка Event Loop

// ПЛОХО — блокирует Event Loop
function blockEventLoop() {
    const start = Date.now();
    while (Date.now() - start < 5000) {
        // Долгий синхронный расчет
    }
}

// ХОРОШО — разбивает на асинхронные части
async function nonBlocking() {
    for (let i = 0; i < 1000; i++) {
        await Promise.resolve(); // Дает возможность выполнить другие задачи
        // Часть расчетов
    }
}

Особенности в Node.js

В Node.js архитектура немного отличается:

// Особые очереди в Node.js
process.nextTick(() => {
    console.log('nextTick — имеет высший приоритет');
});

setImmediate(() => {
    console.log('setImmediate — выполняется после I/O events');
});

Практические следствия для разработчика

1. Предсказуемость порядка выполнения

// Понимание порядка помогает избежать race conditions
element.addEventListener('click', () => {
    Promise.resolve().then(() => console.log('Микрозадача из события'));
    console.log('Синхронный код события');
});

2. Оптимизация производительности

  • Разбиение долгих задач на части
  • Использование requestIdleCallback для фоновых задач
  • Приоритизация микрозадач для более отзывчивого интерфейса

3. Избегание starvation (голодания)

// Проблема: бесконечное добавление микрозадач
function createMicrotaskLoop() {
    Promise.resolve().then(createMicrotaskLoop);
}
// Это заблокирует выполнение макрозадач!

Резюме

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

Как задачи проходят путь до вызова в Event Loop? | PrepBro