Все заметки
20 июля 2026 г.
2 мин
3

Async/Await паттерны которые убивают производительность

Async/Await сделал асинхронный код красивым. Но красота обманчива - под капотом легко написать код который работает в разы медленнее чем должен.

Разберём самые частые ошибки которые встречаются в реальных проектах.

1. Последовательные await там где можно параллельно

Это самая распространённая ошибка. Каждый await блокирует выполнение следующего запроса:

// ✗ Плохо - последовательно, 650мс
async function loadDashboard(userId: string) {
  const user = await fetchUser(userId)     // 200мс
  const orders = await fetchOrders(userId) // 300мс
  const stats = await fetchStats(userId)   // 150мс
  return { user, orders, stats }
}

// ✓ Хорошо - параллельно, 300мс (максимум из трёх)
async function loadDashboard(userId: string) {
  const [user, orders, stats] = await Promise.all([
    fetchUser(userId),
    fetchOrders(userId),
    fetchStats(userId),
  ])
  return { user, orders, stats }
}

Три независимых запроса которые ждут друг друга - это потеря 350мс на каждый вызов. В продакшне это накапливается.

2. Promise.all без обработки ошибок

Promise.all падает целиком если хоть один промис упал. Один сломанный запрос - вся страница пустая:

// ✗ Плохо - один сбой роняет всё
const [user, orders] = await Promise.all([
  fetchUser(userId),
  fetchOrders(userId), // если упал — user тоже не получим
])

// ✓ Хорошо - каждый результат независим
const results = await Promise.allSettled([
  fetchUser(userId),
  fetchOrders(userId),
])

const user = results[0].status === 'fulfilled' ? results[0].value : null
const orders = results[1].status === 'fulfilled' ? results[1].value : []

Promise.allSettled даёт результат каждого промиса независимо - fulfilled или rejected. Страница показывает что смогла загрузить.

3. Потерянные ошибки

Вот реальный кейс: API-запрос падал молча - catch блок просто делал console.log, пользователь видел пустую страницу и не понимал что произошло. Клиент обнаружил баг через неделю.

// ✗ Плохо - ошибка съедается
async function getUser(id: string) {
  try {
    return await fetchUser(id)
  } catch (error) {
    console.log(error) // ошибка никуда не идёт
  }
}

// ✓ Хорошо - ошибка пробрасывается выше
async function getUser(id: string) {
  try {
    return await fetchUser(id)
  } catch (error) {
    console.error('[getUser] Ошибка:', error)
    throw error // пусть вызывающий код решает что делать
  }
}

4. Promise<any> вместо типизированного результата

Это TypeScript-специфичная ошибка которая перечёркивает весь смысл типизации:

// ✗ Плохо - теряем все преимущества TypeScript
async function fetchUser(id: string): Promise<any> {
  const res = await fetch(`/api/users/${id}`)
  return res.json()
}

// ✓ Хорошо - явный тип результата
interface User {
  id: string
  name: string
  email: string
}

async function fetchUser(id: string): Promise<User> {
  const res = await fetch(`/api/users/${id}`)
  if (!res.ok) throw new Error(`Ошибка: ${res.status}`)
  return res.json() as Promise<User>
}

5. Async функции там где они не нужны

Каждая async функция создаёт Promise объект - это не бесплатно. Исследование показало что неоптимальное использование async/await может давать до 14x замедление на горячих путях:

// ✗ Плохо - async без причины
async function add(a: number, b: number) {
  return a + b // зачем Promise если результат синхронный?
}

// ✓ Хорошо - просто синхронная функция
function add(a: number, b: number) {
  return a + b
}

Итог

Async/Await не делает код автоматически правильным - он делает его читаемым. Производительность и надёжность зависят от того как ты им пользуешься. Promise.all вместо последовательных await, Promise.allSettled когда ошибка одного запроса не должна рушить всё, явные типы вместо any, пробрасывание ошибок вместо их поглощения.

Эти паттерны не сложные - просто надо знать где искать проблему. ⚡