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

Первее выполнится timeout или Promise

2.0 Middle🔥 301 комментариев
#JavaScript Core

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

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

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

Приоритет выполнения: setTimeout vs Promise

Этот вопрос затрагивает фундаментальный аспект работы Event Loop в JavaScript. Чтобы дать точный ответ, нужно разделить его на две ситуации: выполнение в основном потоке и сравнение времени выполнения.

1. Базовый принцип Event Loop

JavaScript имеет однопоточную модель выполнения с асинхронной природой. Event Loop управляет порядком выполнения кода через несколько очередей:

  • Call Stack (Стек вызовов) — синхронные операции
  • Microtask Queue (очередь микрозадач) — для Promise.then/catch/finally, queueMicrotask, MutationObserver
  • Macrotask Queue (очередь макрозадач) — для setTimeout/setInterval, setImmediate, обработчиков событий, requestAnimationFrame

Ключевое правило: Event Loop обрабатывает микрозадачи между макрозадачами и очищает всю очередь микрозадач перед переходом к следующей макрозадаче.

2. Сравнительный пример

Рассмотрим классический пример:

console.log('Start');

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

Promise.resolve()
    .then(() => {
        console.log('Promise 1 resolved');
    })
    .then(() => {
        console.log('Promise 2 resolved');
    });

console.log('End');

Результат выполнения:

Start
End
Promise 1 resolved
Promise 2 resolved
Timeout callback

Почему так происходит:

  1. Сначала выполняется синхронный код: 'Start', затем 'End'
  2. setTimeout регистрирует колбэк в Web APIs, а по истечению таймера (даже 0 мс) помещает его в очередь макрозадач
  3. Promise.resolve() создает сразу исполненный промис, а его .then() помещает в очередь микрозадач
  4. После очистки Call Stack, Event Loop сначала выполняет все микрозадачи (Promise колбэки), и только затем переходит к макрозадачам (таймаут)

3. Что выполнится первее — зависит от контекста

Ситуация А: Таймаут с нулевой задержкой и уже разрешенный промис

Если промис уже разрешен (например, через Promise.resolve() или async функция), то его колбэк .then() выполнится раньше таймаута, так как попадет в очередь микрозадач.

Ситуация Б: Асинхронное разрешение промиса

setTimeout(() => console.log('timeout'), 0);

new Promise(resolve => {
    console.log('Promise executor'); // Синхронно!
    // Промис НЕ разрешен сразу
    setTimeout(() => {
        resolve('resolved');
    }, 10);
}).then(result => console.log(result));

Здесь:

  1. 'Promise executor' выполнится синхронно сразу
  2. Таймаут на 0мс сработает первым (через ~4мс минимальная задержка в браузере)
  3. Промис разрешится только через 10мс, поэтому его .then() сработает позже

Ситуация В: Вложенные асинхронные операции

setTimeout(() => console.log('timeout 1'), 0);

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

setTimeout(() => console.log('timeout 2'), 0);

Вывод:

promise 1
promise 2
timeout 1
timeout 2
nested timeout

4. Технические детали

  • Минимальная задержка таймаута: В браузерах минимальная задержка setTimeout4мс (согласно спецификации HTML5), даже если указан 0. В Node.js такой фиксированной задержки нет, но есть свои особенности планировщика.
  • queueMicrotask: Прямой аналог промисов для добавления микрозадач.
  • Разные очереди макрозадач: В реальности существует несколько очередей для макрозадач, которые обрабатываются с разными приоритетами.

Итог

В условиях одинаковой точки старта (оба асинхронных запланированы одновременно):

  1. Promise колбэки (.then/.catch/.finally) выполнятся раньше setTimeout с нулевой задержкой
  2. setTimeout всегда выполнится позже, даже с задержкой 0 мс

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