← Назад к вопросам
Выполнится первее Promise.resolve() или setTimeout с нулевой задержкой
2.2 Middle🔥 272 комментариев
#JavaScript Core
Комментарии (2)
🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Promise.resolve() vs setTimeout(0): Порядок выполнения
Короткий ответ: Promise.resolve() выполнится ПЕРВЫМ, потому что это микротаск, а setTimeout это макротаск. Микротаски ВСЕГДА выполняются перед макротасками.
Основной пример
Promise.resolve().then(() => {
console.log('Promise.resolve() выполнился ПЕРВЫМ');
});
setTimeout(() => {
console.log('setTimeout выполнился ВТОРЫМ');
}, 0);
console.log('Синхронный код');
// ВЫВОД:
// Синхронный код
// Promise.resolve() выполнился ПЕРВЫМ
// setTimeout выполнился ВТОРЫМ
Почему именно в таком порядке?
Это связано с Event Loop в JavaScript. Вот схема:
┌──────────────────────────────────┐
│ 1. CALL STACK (синхронный код) │
│ ├─ console.log('Синхронный..') │
│ └─ [выполнение завершено] │
├──────────────────────────────────┤
│ 2. MICROTASK QUEUE (очередь) │
│ ├─ Promise.resolve().then() │
│ └─ [выполняем это] │
├──────────────────────────────────┤
│ 3. MACROTASK QUEUE (очередь) │
│ ├─ setTimeout(callback, 0) │
│ └─ [выполняем это] │
└──────────────────────────────────┘
Очередь микротасков ВСЕГДА выполняется полностью ПЕРЕД очередью макротасков.
Детальное объяснение: Как работает Event Loop
// Фаза 1: Синхронный код
console.log('START');
// Фаза 2: Микротаск (Promise)
Promise.resolve()
.then(() => console.log('Promise 1'));
.then(() => console.log('Promise 2'));
// Фаза 3: Макротаск (setTimeout)
setTimeout(() => console.log('setTimeout 1'), 0);
// Фаза 4: Еще микротаск
queueMicrotask(() => console.log('queueMicrotask'));
// Фаза 5: Еще макротаск
setTimeout(() => console.log('setTimeout 2'), 0);
console.log('END');
// ════════════════════════════════════════
// ПОРЯДОК ВЫПОЛНЕНИЯ:
// ════════════════════════════════════════
// ✓ Фаза 1: Синхронный код выполнен
// START
// END
// ✓ Фаза 2: Все микротаски (очередь)
// Promise 1
// Promise 2
// queueMicrotask
// ✓ Фаза 3: Первый макротаск
// setTimeout 1
// ✓ Фаза 4: Микротаски после первого макротаска (если есть)
// (микротасков нет)
// ✓ Фаза 5: Второй макротаск
// setTimeout 2
Множественные Promise vs Множественные setTimeout
// Promise.resolve() 1
Promise.resolve().then(() => console.log('Promise 1'));
// setTimeout 1
setTimeout(() => console.log('setTimeout 1'), 0);
// Promise.resolve() 2
Promise.resolve().then(() => console.log('Promise 2'));
// setTimeout 2
setTimeout(() => console.log('setTimeout 2'), 0);
// ВЫВОД:
// Promise 1 <- оба Promise выполняются ПЕРЕД setTimeout
// Promise 2
// setTimeout 1 <- потом оба setTimeout
// setTimeout 2
Микротаски vs Макротаски: Полный список
МИКРОТАСКИ (выполняются в ПЕРВУЮ очередь)
// 1. Promise.resolve(), Promise.reject()
Promise.resolve().then(() => {});
Promise.reject().catch(() => {});
// 2. queueMicrotask
queueMicrotask(() => {});
// 3. MutationObserver
new MutationObserver(() => {}).observe(document.body, {});
// 4. process.nextTick (Node.js)
process.nextTick(() => {});
// 5. async/await (это Promise под капотом)
async function test() {
await promise; // это микротаск
}
МАКРОТАСКИ (выполняются ВО ВТОРУЮ очередь)
// 1. setTimeout
setTimeout(() => {}, 0);
// 2. setInterval
setInterval(() => {}, 0);
// 3. setImmediate (Node.js, не браузер)
setImmediate(() => {});
// 4. requestAnimationFrame (полу-макротаск)
requestAnimationFrame(() => {});
// 5. fetch, XMLHttpRequest (события load/error)
fetch('/api').then(...); // fetch это Promise (микротаск)
Парадокс: setTimeout(0)
Важно понимать, что setTimeout(0) не значит "выполнить сразу":
console.log('START');
setTimeout(() => {
console.log('setTimeout выполнится позже');
}, 0); // Даже с нулевой задержкой!
console.log('END');
// ВЫВОД:
// START
// END
// setTimeout выполнится позже
// setTimeout(0) это минимум 4ms в браузере!
// Браузер может замедлить setTimeout если вкладка не в фокусе
Реальный пример из практики
// Загрузка данных с API
async function fetchData() {
console.log('1. Начало загрузки');
const response = await fetch('/api/data'); // Микротаск
console.log('2. Данные получены');
const data = await response.json(); // Еще микротаск
console.log('3. JSON распарсен');
// Отложить обновку на следующий frame
setTimeout(() => {
console.log('4. Обновление DOM (макротаск)');
updateDOM(data);
}, 0);
}
Promise.resolve().then(() => {
console.log('5. Еще один Promise');
});
fetchData();
// ВЫВОД:
// 1. Начало загрузки
// 2. Данные получены
// 3. JSON распарсен
// 5. Еще один Promise
// 4. Обновление DOM (макротаск)
Проблема: неправильный порядок ожиданий
// ❌ НЕПРАВИЛЬНО: ожидаем что setTimeout выполнится первым
let data = null;
setTimeout(() => {
data = 'loaded'; // Это выполнится вторым!
}, 0);
Promise.resolve().then(() => {
console.log(data); // undefined (первый, до setTimeout)
});
// ✅ ПРАВИЛЬНО: понимаем что Promise выполнится первым
let data = null;
setTimeout(() => {
data = 'loaded';
}, 0);
Promise.resolve().then(() => {
setTimeout(() => {
console.log(data); // 'loaded' (теперь верно)
}, 0);
});
Практический совет для интервью
// Когда вас спрашивают "Что выполнится первым?"
// Запомните порядок:
// 1. СИНХРОННЫЙ КОД
// 2. МИКРОТАСКИ (Promise.resolve, queueMicrotask)
// 3. МАКРОТАСКИ (setTimeout, setInterval)
// Promise.resolve() это МИКРОТАСК
// setTimeout(0) это МАКРОТАСК
// Микротаски выполняются ПЕРЕД макротасками
// Поэтому:
Promise.resolve().then(() => console.log('ПЕРВЫЙ'));
setTimeout(() => console.log('ВТОРОЙ'), 0);
// ВЫВОД: ПЕРВЫЙ, ВТОРОЙ
Сложный пример для полного понимания
console.log('START');
// setTimeout - макротаск
setTimeout(() => {
console.log('setTimeout 1');
// Микротаск внутри макротаска
Promise.resolve().then(() => {
console.log('Promise внутри setTimeout');
});
// Еще макротаск внутри макротаска
setTimeout(() => {
console.log('setTimeout 2 внутри setTimeout 1');
}, 0);
}, 0);
// Promise - микротаск
Promise.resolve().then(() => {
console.log('Promise 1');
// Макротаск внутри Promise
setTimeout(() => {
console.log('setTimeout 3 внутри Promise');
}, 0);
}).then(() => {
console.log('Promise 2');
});
// queueMicrotask - микротаск
queueMicrotask(() => {
console.log('queueMicrotask');
});
console.log('END');
// ПОРЯДОК ВЫПОЛНЕНИЯ:
// START
// END
// ├─ [Все микротаски]
// ├─ Promise 1
// ├─ Promise 2
// ├─ queueMicrotask
// │
// ├─ [Первый макротаск]
// ├─ setTimeout 1
// ├─ [Микротаски после первого макротаска]
// ├─ Promise внутри setTimeout
// │
// ├─ [Второй макротаск]
// ├─ setTimeout 3 внутри Promise
// │
// ├─ [Третий макротаск]
// └─ setTimeout 2 внутри setTimeout 1
Итоговый ответ для интервью
Promise.resolve() выполнится ПЕРВЫМ.
Это потому что Promise.resolve().then() это микротаск, а setTimeout это макротаск. Event Loop в JavaScript ВСЕГДА выполняет все микротаски перед тем как перейти к макротаскам.
Порядок в Event Loop:
- Синхронный код
- ВСЕ микротаски (Promise, queueMicrotask)
- Первый макротаск (setTimeout)
- ВСЕ микротаски между макротасками
- Следующий макротаск
Даже setTimeout(0) с нулевой задержкой будет выполнен ПОСЛЕ Promise.resolve().