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

Как вылетают задачи из очереди задач?

2.2 Middle🔥 191 комментариев
#JavaScript Core

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

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

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

Как задачи покидают очередь задач (Task Queue) в Event Loop

В контексте JavaScript и его Event Loop, "очередь задач" (Task Queue, также известная как Callback Queue или Macrotask Queue) — это критически важный механизм, обеспечивающий асинхронное выполнение кода в однопоточном окружении (например, в браузере или Node.js). Выход задач из этой очереди — строго регламентированный процесс, неразрывно связанный с циклом событий.

Основной принцип: Цикл событий (Event Loop)

Event Loop — это бесконечный цикл, который непрерывно проверяет состояние стеков и очередей. Его основная работа заключается в следующем:

  1. Выполнить код из стека вызовов (Call Stack) до его опустошения.
  2. Проверить очередь микрозадач (Microtask Queue). Если она не пуста, выполнить все задачи из неё до полного опустошения. Сюда входят промисы (Promise.then/catch/finally), queueMicrotask(), MutationObserver и некоторые другие API.
  3. Проверить очередь задач (Task Queue). Если стек вызовов пуст и очередь микрозадач пуста, взять первую задачу из очереди задач и поместить её в стек вызовов для выполнения.
  4. Перейти к рендерингу (если в браузере), а затем вернуться к шагу 1.

Таким образом, задача "вылетает" (де-энкапсулируется и выполняется) из очереди задач только при соблюдении трёх условий:

  • Стек вызовов полностью пуст (текущий синхронный код завершён).
  • Очередь микрозадач полностью пуста.
  • Данная задача находится в начале очереди (очереди работают по принципу FIFO - First In, First Out).

Ключевые источники задач для Task Queue

Задачи в эту очередь помещают следующие Web API и асинхронные операции:

  • setTimeout() и setInterval()
  • Обработчики событий DOM (click, keydown, load и т.д.)
  • Сетевые запросы (fetch, XMLHttpRequest)
  • Операции ввода/вывода в Node.js (файловая система, сеть)

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

Рассмотрим код, который наглядно демонстрирует приоритеты:

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

// Таск (макрозадача)
setTimeout(() => console.log('6. Таймаут 0ms'), 0);

// Микрозадача
Promise.resolve()
  .then(() => {
    console.log('3. Промис 1');
    // Ещё одна микрозадача, добавленная во время выполнения микрозадачи
    queueMicrotask(() => console.log('4. queueMicrotask внутри then'));
  })
  .then(() => console.log('5. Промис 2'));

// Другой таск (макрозадача)
setTimeout(() => console.log('7. Таймаут 0ms - второй'), 0);

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

// Результат выполнения:
// 1. Синхронный код старт
// 2. Синхронный код конец
// 3. Промис 1
// 4. queueMicrotask внутри then
// 5. Промис 2
// 6. Таймаут 0ms
// 7. Таймаут 0ms - второй

Пошаговый разбор:

  1. Выполняется весь синхронный код (console.log 1 и 2). Стек вызовов очищается.
  2. Event Loop проверяет очередь микрозадач. Там находятся коллбэки из Promise.resolve().then(...). Они выполняются все до конца (лог 3, 4, 5). Очередь микрозадач опустошается.
  3. Event Loop теперь проверяет очередь задач (Task Queue). Там ждут два коллбека от setTimeout.
  4. Поскольку стек пуст и микрозадач нет, первая задача "вылетает" из очереди и выполняется (лог 6). После её завершения стек снова пуст.
  5. Event Loop начинает новую итерацию. Он снова проверяет очередь микрозадач (она пуста). Затем проверяет очередь задач и забирает следующую задачу (лог 7).

Важные нюансы и современные особенности

  • Не одна очередь, а несколько. В современных браузерах существует несколько Task Queues (для таймеров, событий, сетевых запросов), каждая со своим типом и приоритетом. Event Loop может выбирать задачу из очереди с более высоким приоритетом, но в рамках одного типа порядок FIFO сохраняется.
  • Рендеринг. После выполнения каждой задачи (макрозадачи) и всех микрозадач браузер может выполнить перерисовку (rendering), если это необходимо. Это значит, что долгие задачи блокируют рендеринг и взаимодействие с интерфейсом.
  • setImmediate в Node.js также является макрозадачей, но работает в своей собственной фазе цикла событий Node.js, которая отличается от браузерной.
  • requestAnimationFrame — это не задача и не микрозадача. Его коллбэк выполняется на этапе рендеринга, перед отрисовкой кадра, и обычно имеет более высокий приоритет, чем задачи из Task Queue.

Заключение

Задачи "вылетают" из очереди задач не хаотично, а по строгим правилам Event Loop: только когда стек вызовов и очередь микрозадач пусты, и строго по одной за итерацию цикла (после чего цикл начинается заново). Понимание этого механизма — основа для написания эффективного, неблокирующего асинхронного кода, корректной работы с таймерами, событиями и предсказуемого управления порядком выполнения операций.

Как вылетают задачи из очереди задач? | PrepBro