← Назад к вопросам
Как запустится setTimeout с задержкой в 1 секунду в Event Loop если 1 секунда еще не прошла?
1.8 Middle🔥 251 комментариев
#JavaScript Core
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как запустится setTimeout с задержкой в 1 секунду если 1 секунда еще не прошла
Поднимаем основы
Сначала нужно понять разницу между главным потоком и асинхронными операциями. JavaScript выполняется в одном потоке, но setTimeout работает по-другому благодаря браузеру.
Архитектура Event Loop
JavaScript Engine (V8)
↓
Main Thread (Call Stack) — выполняет синхронный код
↓
Web APIs (предоставляет браузер) — таймеры, сетевые запросы
↓
Task Queue (Макротаск очередь) — setTimeout, setInterval
Microtask Queue (Микротаск очередь) — Promises, async/await
↓
Event Loop — управляет очередями
Что происходит когда вы вызываете setTimeout
Шаг 1: Регистрация
console.log("Начало");
setTimeout(() => {
console.log("Через 1 секунду");
}, 1000);
console.log("Конец");
// Вывод:
// Начало
// Конец
// (спустя 1 сек)
// Через 1 секунду
Что происходит:
- Начало — выполняется синхронно
setTimeout()— передается в Web API (браузер), основной поток не ждёт- Конец — выполняется сразу же
- Спустя 1 сек — callback из setTimeout добавляется в Task Queue
- Event Loop замечает, что Call Stack пуст — добавляет callback из очереди
- Через 1 секунду — выполняется
Диаграмма Event Loop
Call Stack Web APIs Task Queue
┌──────────────┐ ┌──────────┐ ┌──────────┐
│ console.log │ │setTimeout│ │ │
│ Начало │ │ 1000ms │ │ │
└──────────────┘ └──────────┘ └──────────┘
↓ ↓ ↓
setTimeout() вызван Таймер запущен Ждёт 1 сек
↓
┌──────────────┐ ┌──────────┐ ┌──────────┐
│ console.log │ │setTimeout│ │ │
│ Конец │ │ 990ms │ │ │
└──────────────┘ └──────────┘ └──────────┘
↓
Call Stack пуст! ↓ ↓
↓ Время истекло! callback добавлен
┌──────────────┐ ┌──────────┐ ┌──────────┐
│ │ │ │ │callback()│
│ (пусто) │ │ 0ms │ │ │
└──────────────┘ └──────────┘ └──────────┘
↑ ↑ ↓
Event Loop видит Web API отправляет Event Loop берёт
пусть! Берет из в очередь и выполняет
очереди
Ключевой момент: АСИНХРОННОСТЬ
setTimeout не блокирует основной поток. Это асинхронная операция:
const start = Date.now();
setTimeout(() => {
console.log("Прошло", Date.now() - start, "ms");
}, 1000);
// Тяжёлые вычисления (блокируют основной поток)
for (let i = 0; i < 2000000000; i++) {}
console.log("Синхронный код завершён");
// Вывод:
// Синхронный код завершён (через ~3 сек)
// Прошло 3000+ ms
Что произошло:
setTimeout(1000)зарегистрирован в Web API- Синхронный for-loop выполняется ~2 сек
- Основной поток занят, Event Loop ждёт
- When for-loop завершена (3+ сек прошло)
- Call Stack пуст
- Event Loop видит callback в Task Queue
- Callback выполняется (прошло ~3 сек, а не 1)
Микротаски vs Макротаски
console.log("1. Синхронный код");
setTimeout(() => {
console.log("4. setTimeout (макротаск)");
}, 0);
Promise.resolve()
.then(() => {
console.log("3. Promise (микротаск)");
});
console.log("2. Ещё синхронный код");
// Вывод:
// 1. Синхронный код
// 2. Ещё синхронный код
// 3. Promise (микротаск)
// 4. setTimeout (макротаск)
Порядок выполнения:
- Call Stack — выполнить весь синхронный код
- Microtask Queue — выполнить ВСЕ микротаски (Promises, async/await)
- Rendering — обновить DOM
- Macrotask Queue — выполнить ОДИН макротаск (setTimeout, setInterval)
- Вернуться к шагу 2
Практический пример: задержка на 0ms
console.log("Начало");
setTimeout(() => {
console.log("setTimeout 0");
}, 0);
console.log("Конец");
// Вывод:
// Начало
// Конец
// setTimeout 0
Именно поэтому setTimeout(fn, 0) используется для дефера (отложить до следующего цикла Event Loop):
// Плохо: блокирует интерфейс
function heavyComputation() {
for (let i = 0; i < 1000000000; i++) {}
}
button.addEventListener("click", heavyComputation);
// Хорошо: не блокирует
button.addEventListener("click", () => {
setTimeout(heavyComputation, 0);
});
Внутри Node.js (если вопрос о backend)
В Node.js setTimeout работает аналогично, но есть libuv, который управляет таймерами и асинхронностью. Главная идея та же — асинхронность не блокирует основной поток.
Вывод
setTimeout не ждёт 1 секунду в основном потоке. Вместо этого:
- Callback регистрируется в Web API браузера
- Основной поток продолжает работать (асинхронность)
- Спустя 1 сек Web API добавляет callback в Task Queue
- Когда Call Stack пуст, Event Loop берёт callback и выполняет
- Если основной поток занят, callback ждёт (отсюда задержки > 1 сек)
Это неблокирующая асинхронность — ключевой принцип JavaScript.