Async: Promise и await
Event loop, async/await, параллельный запуск через Promise.all.
Обновлено
Что значит синхронно и асинхронно
Синхронно = каждая строка ждёт, пока закончит предыдущая. Асинхронно = вы запускаете медленную операцию (сетевой запрос, таймер, чтение файла) и продолжаете делать другое; результат придёт позже.
Почему это важно: JS в браузере работает в одном потоке — том же, который рисует страницу и отвечает на клики. Если бы он синхронно ждал запрос 2 секунды, страница зависла бы на 2 секунды.
Event loop — как это работает
- Call stack — код, который выполняется прямо сейчас.
- Медленные операции передаются браузеру (сеть, таймеры), он выполняет их параллельно.
- Когда одна заканчивается, её колбэк попадает в очередь.
- Event loop берёт из очереди, только когда стек пуст.
Есть две очереди с разным приоритетом:
| Очередь | Что попадает | Приоритет |
|---|---|---|
| microtask | .then у Promise, await, queueMicrotask |
все выполняются до следующей macrotask |
| macrotask | setTimeout, setInterval, события, I/O |
одна за оборот |
console.log(1)
setTimeout(() => console.log(4)) // macrotask
Promise.resolve().then(() => console.log(3)) // microtask
console.log(2)
// 1, 2, 3, 4Что такое Promise
Promise — объект, который представляет значение, которое ещё придёт, — как чек заказа: еды пока нет, но есть то, чего можно ждать.
У Promise 3 состояния:
| Состояние | Означает |
|---|---|
pending |
ещё выполняется |
fulfilled |
готово, есть значение |
rejected |
не удалось, есть ошибка |
Когда Promise завершился (fulfilled или rejected), состояние больше не меняется.
const wait = ms => new Promise(resolve => setTimeout(resolve, ms))
fetch('/api/user')
.then(res => res.json()) // выполняется при успехе
.then(user => render(user))
.catch(err => showError(err)) // выполняется при ошибке в любом месте цепочкиasync / await — то же самое, но как синхронный код
async function loadUser(id) {
try {
const res = await fetch(`/api/users/${id}`) // «ставит на паузу» функцию до ответа
return await res.json()
} catch (error) {
return null
}
}asyncперед функцией → она всегда возвращает Promise.await→ ждёт Promise, не блокируя страницу («стоит» только текущая функция).- Ошибки ловятся обычным
try/catch.
.then() |
async/await |
|
|---|---|---|
| Читаемость | цепочки, колбэки | код сверху вниз |
| Ошибки | .catch() |
try/catch |
| Когда | короткие цепочки вне async-функций | по умолчанию |
Последовательно или параллельно — самая частая оптимизация
// ✗ последовательно — второй стартует после первого: 2 × время
const user = await getUser()
const posts = await getPosts()
// ✓ параллельно — оба стартуют сразу
const [user, posts] = await Promise.all([getUser(), getPosts()])Последовательно — только когда второй запрос зависит от результата первого.
Комбинаторы Promise — виды и когда
| Метод | Завершается, когда | Если один упал | Когда |
|---|---|---|---|
Promise.all |
все успешны | падает сразу | нужны все результаты |
Promise.allSettled |
все завершились | не падает; получаете статус каждого | нужны частичные результаты |
Promise.race |
первый завершился (как угодно) | зависит от первого | таймаут |
Promise.any |
первый успешный | падает, только если упали все | несколько источников, подойдёт любой |
Классические ошибки
forEachсasyncне ждёт →for...of(последовательно) илиPromise.all(arr.map(...))(параллельно).- Забыли
await→ вместо значения Promise (user.nameравноundefined). - Promise без
catch/try→unhandledrejection. awaitв цикле, когда запросы независимы, → медленно.
Коротко
- В JS один поток; медленные операции асинхронны, чтобы страница не зависала.
- Promise = будущее значение: pending → fulfilled / rejected.
async/await+try/catch;Promise.allдля параллельной работы.