Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Event Loop: Очередь и асинхронность
Event Loop - это сердце асинхронности в JavaScript. Он управляет очередью задач и определяет, какая функция выполнится дальше.
Основная структура Event Loop
JavaScript имеет три основных компонента:
// 1. CALL STACK (Стек вызовов) - синхронный код
// 2. TASK QUEUE (Очередь задач) - асинхронные задачи
// 3. EVENT LOOP - управляет выполнением
// Процесс:
// 1. Event Loop проверяет Call Stack
// 2. Если Stack пуст, Event Loop берёт первую задачу из Queue
// 3. Если Stack не пуст, ждёт пока он освободится
Пример выполнения
console.log("1"); // синхронно
setTimeout(() => {
console.log("2"); // асинхронно (Task Queue)
}, 0);
Promise.resolve().then(() => {
console.log("3"); // микротаск (Microtask Queue)
});
console.log("4"); // синхронно
// Результат:
// 1
// 4
// 3
// 2
// Почему так?
// 1. console.log("1") выполняется сразу (Call Stack)
// 2. setTimeout отправляется в Task Queue
// 3. Promise.then отправляется в Microtask Queue
// 4. console.log("4") выполняется сразу (Call Stack)
// 5. Event Loop видит пустой Stack, проверяет Microtask Queue (есть!)
// 6. Выполняет console.log("3")
// 7. Event Loop видит пустой Stack, проверяет Task Queue (есть!)
// 8. Выполняет console.log("2")
Две очереди: Task Queue и Microtask Queue
Task Queue (Макротаски)
- setTimeout, setInterval, setImmediate
- UI рендеринг
- Обработчики событий (click, scroll и т.д.)
Microtask Queue (Микротаски)
- Promise.then, Promise.catch, Promise.finally
- MutationObserver
- queueMicrotask()
console.log("1"); // Stack
setTimeout(() => {
console.log("2"); // Task Queue (макротаска)
}, 0);
Promise.resolve()
.then(() => console.log("3")) // Microtask Queue
.then(() => console.log("4")); // Microtask Queue
console.log("5"); // Stack
// Результат:
// 1
// 5
// 3
// 4
// 2
// Порядок приоритета:
// 1. Синхронный код (Call Stack) -> 1, 5
// 2. Все микротаски -> 3, 4
// 3. Первая макротаска -> 2
// 4. Если есть ещё макротаски, повторить с шага 2
Подробный пример: Promise vs setTimeout
const promise = Promise.resolve("Promise");
const timeout = setTimeout(() => console.log("setTimeout"), 0);
promise.then((value) => {
console.log(value);
clearTimeout(timeout); // Попытка очистить setTimeout
});
// Результат:
// Promise
// setTimeout
// Почему setTimeout всё ещё выполнится?
// Promise.then это микротаска - выполняется ДО setTimeout (которая макротаска)
// Но когда мы в Promise.then пытаемся clearTimeout, setTimeout уже в Task Queue
// ClearTimeout не может удалить задачу, которая уже запланирована на выполнение
Визуальное представление
Call Stack <- Выполняется первым (синхронный код)
|
v
Event Loop проверяет
|
v
Microtask Queue <- Выполняется вторым (Promise, MutationObserver)
|
v
Stack пуст?
|
v
Task Queue <- Выполняется третьим (setTimeout, setInterval)
|
v
Вернуться в Call Stack
Практический пример: Сложный сценарий
console.log("Start");
setTimeout(() => {
console.log("setTimeout 1");
Promise.resolve().then(() => console.log("Promise inside setTimeout"));
}, 0);
Promise.resolve()
.then(() => {
console.log("Promise 1");
setTimeout(() => console.log("setTimeout inside Promise"), 0);
})
.then(() => console.log("Promise 2"));
console.log("End");
// Результат:
// Start
// End
// Promise 1
// Promise 2
// setTimeout 1
// Promise inside setTimeout
// setTimeout inside Promise
// Объяснение:
// 1. console.log("Start") - Stack
// 2. setTimeout -> Task Queue
// 3. Promise.then -> Microtask Queue
// 4. console.log("End") - Stack
// 5. Stack пуст, выполняем микротаски: Promise 1, Promise 2
// 6. Во время Promise 2 создаётся ещё один setTimeout -> Task Queue
// 7. Выполняем макротаски: setTimeout 1
// 8. Во время setTimeout 1 создаётся Promise -> Microtask Queue
// 9. Выполняем все оставшиеся микротаски: Promise inside setTimeout
// 10. Выполняем следующую макротаску: setTimeout inside Promise
Оптимизация и best practices
1. Избегай цепочек setTimeout
// ❌ Плохо - медленно
setTimeout(() => {
setTimeout(() => {
setTimeout(() => {
console.log("Done");
}, 0);
}, 0);
}, 0);
// ✅ Хорошо - используй Promise
Promise.resolve()
.then(() => console.log("Done"))
.catch(console.error);
2. Используй requestAnimationFrame для UI обновлений
// ❌ Плохо - может блокировать рендеринг
setInterval(() => {
updateDOM();
}, 16);
// ✅ Хорошо - синхронизирован с браузером
function updateUI() {
requestAnimationFrame(() => {
updateDOM();
updateUI(); // Рекурсивный вызов
});
}
updateUI();
3. Правильное использование Promise и async/await
// ✅ Хорошо - async/await читаемее и безопаснее
async function fetchData() {
try {
const response = await fetch("/api/data");
const data = await response.json();
console.log(data);
} catch (error) {
console.error("Ошибка:", error);
}
}
// Это еквивалентно:
// Promise.then().catch()
// но более понятно
Ключ к пониманию Event Loop: сначала выполняется синхронный код, потом микротаски, потом макротаски.