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

Какие знаешь очереди в событийном цикле?

2.3 Middle🔥 121 комментариев
#JavaScript Core

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

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

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

Очереди в событийном цикле JavaScript

В событийном цикле JavaScript существует несколько очередей задач (task queues), которые играют ключевую роль в асинхронном выполнении кода. Важно понимать, что спецификация ECMAScript не определяет конкретные очереди, а вводит абстракцию очереди заданий (Job Queue). Однако в браузерных средах реализация событийного цикла (например, в движке V8) обычно включает следующие очереди:

Основные типы очередей

  1. Очередь макрозадач (Task Queue / Macro-Task Queue)
    *   Это основная очередь, содержащая **макрозадачи (macrotasks)**.
    *   **Примеры:** обработчики событий (`click`, `keypress`), коллбэки `setTimeout` и `setInterval`, операции ввода-вывода (I/O), парсинг HTML, выполнение основного кода (script evaluation).
    *   На каждой итерации событийного цикла из этой очереди извлекается и выполняется **одна** задача.

  1. Очередь микрозадач (Microtask Queue)
    *   Это очередь с более высоким приоритетом, содержащая **микрозадачи (microtasks)**.
    *   **Примеры:** коллбэки промисов (`.then()`, `.catch()`, `.finally()`), `queueMicrotask()`, `MutationObserver`.
    *   После выполнения каждой макрозадачи (или основного скрипта) событийный цикл **полностью опустошает** очередь микрозадач перед переходом к следующей макрозадаче.

  1. Очередь анимаций (Animation Frames Queue)
    *   Специальная очередь, связанная с `requestAnimationFrame`. Она выполняется на этапе **рендеринга (rendering pipeline)** событийного цикла, между выполнением микрозадач и следующей макрозадачей.
    *   Позволяет выполнять код перед перерисовкой кадра, что критично для плавной анимации.

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

Ключевой принцип: микрозадачи имеют наивысший приоритет после текущей выполняемой задачи. Вот типичный порядок итерации событийного цикла:

  1. Выполняется одна макрозадача (например, основной скрипт или коллбэк setTimeout).
  2. Выполняются все доступные микрозадачи из очереди (пока она не опустеет).
  3. При необходимости происходит рендеринг (вызов requestAnimationFrame, вычисление стилей, layout, paint).
  4. Цикл повторяется для следующей макрозадачи.

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

Рассмотрим пример, демонстрирующий взаимодействие очередей:

console.log('1. Начало (макрозадача: основной скрипт)');

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

Promise.resolve()
    .then(() => {
        console.log('3. Промис 1 (микрозадача)');
        return Promise.resolve();
    })
    .then(() => {
        console.log('5. Промис 2 (вложенная микрозадача)');
    });

queueMicrotask(() => {
    console.log('6. queueMicrotask (микрозадача)');
});

console.log('2. Конец основной макрозадачи');

// Результат выполнения:
// 1. Начало (макрозадача: основной скрипт)
// 2. Конец основной макрозадачи
// 3. Промис 1 (микрозадача)
// 5. Промис 2 (вложенная микрозадача)
// 6. queueMicrotask (микрозадача)
// 4. setTimeout (макрозадача)

Пояснение:

  • Сначала выполняется весь синхронный код (макрозадача "основной скрипт") — вывод 1 и 2.
  • Затем движок опустошает очередь микрозадач: выполняются все коллбэки промисов и queueMicrotask (вывод 3, 5, 6). Обратите внимание, что микрозадачи, созданные во время выполнения других микрозадач, также выполняются в текущей итерации.
  • Только после этого из очереди макрозадач извлекается и выполняется коллбэк setTimeout (вывод 4).

Важные нюансы

  • Несколько очередей макрозадач: Браузеры могут использовать несколько очередей для макрозадач разного типа (например, отдельно для событий, таймеров, сетевых запросов) для управления приоритетами, но это внутренняя оптимизация, не меняющая общую логику цикла.
  • requestAnimationFrame: Его коллбэки выполняются не в очереди микрозадач, а на этапе обновления отображения, что обеспечивает синхронизацию с частотой обновления экрана.
  • Блокирующие операции: Длительные синхронные операции в макрозадачах блокируют не только цикл событий, но и рендеринг, что приводит к "подвисанию" интерфейса.

Вывод: Понимание различий между очередями и приоритетов выполнения (макрозадачи → все микрозадачи → рендеринг) является фундаментальным для написания корректного асинхронного кода, управления отзывчивостью интерфейса и предотвращения тонких ошибок, связанных с порядком выполнения.

Какие знаешь очереди в событийном цикле? | PrepBro