async/await

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.


Частые ошибки #

  1. Забыли await — в переменной промис, а не данные:
const invoice = requestInvoice('ord-1') // Promise, не счёт
const total = invoice.total // undefined
  1. forEach с async-колбэкомawait внутри не ждётся снаружи:
// плохо: цикл «закончится» до завершения колбэков
ids.forEach(async (id) => {
  await fetchTrack(id)
})

// лучше: for...of или Promise.all
for (const id of ids) {
  await fetchTrack(id)
}
  1. Нет try/catch на верхнем уровне вызова — необработанный reject.

Итог #

  • async возвращает промис; await разворачивает его в значение или бросает ошибку.
  • try/catch/finally — естественная обработка сбоев в линейном коде.
  • Независимые задачи запускайте параллельно через Promise.all, не цепочкой await.
  • Проверяйте, что действительно await-ите промисы и не используете async внутри forEach без координации.

См. также #

Часто спрашивают

Что выбрать — then или async/await?
async/await обычно читается проще для последовательного кода; then удобен для коротких цепочек и комбинирования с Promise API. Подробнее
Чем Promise.all отличается от allSettled?
all падает при первой ошибке; allSettled дожидается всех и отдаёт статус каждого результата. Подробнее