JavaScript & TypeScriptМодуль 5: Асинхронность
Event Loop
Как JavaScript выполняет код
Цель урока
В этом уроке ты научишься:
- Понимать однопоточность JavaScript
- Разбираться в Call Stack и очередях
- Предсказывать порядок выполнения
Однопоточность
JavaScript выполняет код в одном потоке. Одновременно может выполняться только одна операция.
console.log("Первый");
console.log("Второй");
console.log("Третий");
// Всегда в этом порядкеCall Stack
Стек вызовов — структура, отслеживающая выполнение функций:
function first() {
console.log("first");
second();
}
function second() {
console.log("second");
third();
}
function third() {
console.log("third");
}
first();
// Стек:
// 1. first() добавляется
// 2. console.log("first") выполняется
// 3. second() добавляется
// 4. console.log("second") выполняется
// 5. third() добавляется
// 6. console.log("third") выполняется
// 7. third() удаляется
// 8. second() удаляется
// 9. first() удаляетсяWeb APIs
Браузер предоставляет API для асинхронных операций:
setTimeout,setIntervalfetch- DOM события
- и другие
console.log("Начало");
setTimeout(() => {
console.log("Таймер");
}, 0);
console.log("Конец");
// Вывод:
// Начало
// Конец
// ТаймерПочему так?
Даже с задержкой 0мс, callback попадает в очередь и ждёт, пока стек освободится.
Task Queue (Macrotasks)
Очередь макрозадач — сюда попадают:
- Callbacks от
setTimeout,setInterval - События DOM
- I/O операции
console.log("1");
setTimeout(() => console.log("2"), 0);
setTimeout(() => console.log("3"), 0);
console.log("4");
// Вывод: 1, 4, 2, 3Microtask Queue
Очередь микрозадач — приоритетнее макрозадач:
- Callbacks от Promise (
.then,.catch,.finally) queueMicrotask()MutationObserver
console.log("1");
setTimeout(() => console.log("2"), 0);
Promise.resolve().then(() => console.log("3"));
console.log("4");
// Вывод: 1, 4, 3, 2Порядок выполнения
- Выполняется синхронный код (Call Stack)
- Выполняются все микрозадачи
- Выполняется одна макрозадача
- Повторяется с пункта 2
console.log("1");
setTimeout(() => {
console.log("2");
Promise.resolve().then(() => console.log("3"));
}, 0);
Promise.resolve().then(() => {
console.log("4");
setTimeout(() => console.log("5"), 0);
});
console.log("6");
// Вывод: 1, 6, 4, 2, 3, 5Практический пример
console.log("Скрипт начат");
setTimeout(() => {
console.log("setTimeout 1");
}, 0);
Promise.resolve()
.then(() => console.log("Promise 1"))
.then(() => console.log("Promise 2"));
setTimeout(() => {
console.log("setTimeout 2");
}, 0);
console.log("Скрипт завершён");
// Вывод:
// Скрипт начат
// Скрипт завершён
// Promise 1
// Promise 2
// setTimeout 1
// setTimeout 2Блокировка Event Loop
Тяжёлые синхронные операции блокируют весь интерфейс:
// Плохо — блокирует на 5 секунд
function heavyTask() {
let start = Date.now();
while (Date.now() - start < 5000) {
// блокируем
}
console.log("Готово");
}
heavyTask();
// Страница "зависает" на 5 секундИзбегай блокировки
Разбивай тяжёлые задачи на части или используй Web Workers.
Итого
| Очередь | Примеры | Приоритет |
|---|---|---|
| Call Stack | Синхронный код | Высший |
| Microtasks | Promise, queueMicrotask | Высокий |
| Macrotasks | setTimeout, события | Обычный |
Практика
Задание 1: Предскажи вывод
Задача: Определи порядок вывода.
Запустите код для проверки
Loading...
Ваш вывод:
Ожидаемый результат:
A D C B
Задание 2: Сложный пример
Задача: Определи порядок вывода.
Запустите код для проверки
Loading...
Ваш вывод:
Ожидаемый результат:
5 2 4 1 3
Проверь себя
- Что такое Call Stack?
- Чем микрозадачи отличаются от макрозадач?
- Почему
setTimeout(fn, 0)не выполняется сразу?