async/await #
async/await — синтаксис для работы с промисами: async-функция всегда возвращает промис, а await приостанавливает выполнение функции до разрешения переданного промиса (без блокировки главного потока).
async-функция #
async function getOrderSummary(orderId) {
return { orderId, status: 'packed' }
}
getOrderSummary('ord-12').then((data) => console.log(data.status))
Явный return оборачивается в Promise.resolve. throw внутри async — в Promise.reject.
async function fail() {
throw new Error('Склад закрыт')
}
fail().catch((err) => console.log(err.message))
await #
await можно использовать только внутри async-функции (или на верхнем уровне модуля в современных средах).
function delay(ms, value) {
return new Promise((resolve) => setTimeout(() => resolve(value), ms))
}
async function buildCheckoutTimeline(orderId) {
const reserved = await delay(100, { orderId, step: 'reserved' })
const paid = await delay(150, { ...reserved, step: 'paid' })
const shipped = await delay(80, { ...paid, step: 'shipped' })
return shipped
}
buildCheckoutTimeline('ord-77').then(console.log)
// { orderId: 'ord-77', step: 'shipped' }
Код читается сверху вниз, как синхронный, но между await управление возвращается в событийный цикл.
try/catch с await #
Ошибки отклонённого промиса при await ловятся обычным try/catch:
async function payOrder(cart) {
try {
const invoice = await requestInvoice(cart.orderId)
const charged = await chargePayment(invoice)
return charged
} catch (err) {
console.error('Оплата:', err.message)
return null
} finally {
hideSpinner()
}
}
Это прямой аналог .then().catch().finally() для линейной логики.
Параллель vs последовательность #
Подряд идущие await суммируют время:
// ~400 мс — запросы один за другим
async function sequential() {
const a = await fetchTrack('a')
const b = await fetchTrack('b')
return [a, b]
}
Для независимых операций запускайте промисы сразу, ждите Promise.all:
// ~250 мс — ограничено самым медленным
async function parallel() {
const p1 = fetchTrack('a')
const p2 = fetchTrack('b')
return Promise.all([p1, p2])
}
Правило: если шаг B не нуждается в результате A — не await A перед стартом B.
await на не-промисе #
await оборачивает значение в Promise.resolve:
async function demo() {
const n = await 5
return n + 1
}
demo().then(console.log) // 6
Полезно в универсальных функциях, принимающих «значение или промис».
Стрелочные async #
const loadMeta = async (trackId) => {
const track = await fetchTrack(trackId)
return { ...track, loadedAt: Date.now() }
}
Ограничения те же: нет собственного arguments как у обычной function, лексический this.
Частые ошибки #
- Забыли
await— в переменной промис, а не данные:
const invoice = requestInvoice('ord-1') // Promise, не счёт
const total = invoice.total // undefined
forEachс async-колбэком —awaitвнутри не ждётся снаружи:
// плохо: цикл «закончится» до завершения колбэков
ids.forEach(async (id) => {
await fetchTrack(id)
})
// лучше: for...of или Promise.all
for (const id of ids) {
await fetchTrack(id)
}
- Нет try/catch на верхнем уровне вызова — необработанный reject.
Итог #
asyncвозвращает промис;awaitразворачивает его в значение или бросает ошибку.try/catch/finally— естественная обработка сбоев в линейном коде.- Независимые задачи запускайте параллельно через
Promise.all, не цепочкойawait. - Проверяйте, что действительно
await-ите промисы и не используетеasyncвнутриforEachбез координации.