Асинхронность: callbacks, Promise, async/await

↑ 2.1 JavaScript: продвинутый · ← Предыдущая · → Следующая

Обязательно~3 мин чтенияУровень: middle

Зачем это на собесе

Асинхронность — основа фронтенда. Ждут понимания состояний Promise, комбинаторов и обработки ошибок.

Объяснение

Callbacks приводят к «аду колбэков» и сложной обработке ошибок. Promise — объект будущего результата: pending → fulfilled | rejected (состояние необратимо).

const p = new Promise((resolve, reject) => setTimeout(() => resolve(42), 100));
p.then(v => v * 2).then(console.log).catch(console.error).finally(cleanup);
 
fetch("/api").then(r => r.json());     // цепочка: then возвращает новый Promise
КомбинаторРезультат
Promise.all([...])все успешны (или первая ошибка)
Promise.allSettledрезультаты всех со статусами
Promise.raceпервый завершившийся
Promise.anyпервый успешный (или AggregateError)
Promise.withResolvers()внешние resolve/reject

async/await — синтаксис над Promise:

async function load(ids, signal) {
  try {
    const [user, orders] = await Promise.all([api.user(ids.u, { signal }), api.orders(ids.u, { signal })]);   // параллельно
    for (const id of ids.items) await process(id);                                                              // последовательно
    return { user, orders };
  } catch (e) {
    if (e.name === "AbortError") return null;
    throw e;
  }
}

Правила:

  • async-функция всегда возвращает Promise; await приостанавливает функцию (не поток).
  • await в цикле выполняет последовательно; для параллельности — Promise.all.
  • Ошибка в async — отклонённый Promise; try/catch вокруг await.
  • Продолжение после await — микрозадача (см. Event Loop: микро- и макрозадачи).

Нюансы и подводные камни

  • Забытый await — «плавающий» промис без обработки ошибок (unhandledrejection).
  • forEach(async …) не ждёт завершения.
  • Promise.all отклоняется при первой ошибке, остальные продолжают выполняться.
  • Отмена промисов невозможна: используйте AbortController и передавайте signal.
  • return await внутри try нужен для перехвата ошибки; вне try избыточен.

Практика

  1. Перепишите вложенные коллбэки на Promise, затем на async/await.
  2. Реализуйте retry(fn, n) и timeout(promise, ms).
  3. Распараллельте три независимых запроса и обработайте частичные ошибки через allSettled.

Вопросы с ответами

Связанные темы