try/catch и Error

try/catch и Error #

Ошибка в JavaScript прерывает нормальный поток выполнения. Конструкция try/catch/finally перехватывает исключение, даёт обработать его и при необходимости гарантировать завершающий код в finally.


try, catch, finally #

function parseQuantity(raw) {
  try {
    const value = Number(raw)
    if (!Number.isInteger(value) || value < 1) {
      throw new Error('Количество должно быть целым числом ≥ 1')
    }
    return value
  } catch (err) {
    console.error('Некорректный ввод:', err.message)
    return 1
  } finally {
    // выполнится всегда — и при успехе, и при throw
  }
}

parseQuantity('3') // 3
parseQuantity('0.5') // 1, в консоли сообщение об ошибке
  • try — блок, где ошибка может возникнуть.
  • catch (err) — обработчик; err обычно экземпляр Error или его подкласса.
  • finally — код, который выполняется после try/catch независимо от исхода (кроме фатальных сбоев процесса).

return внутри try или catch откладывает выполнение finally до выхода из функции, но finally всё равно отработает перед фактическим возвратом.


throw #

Ключевое слово throw создаёт исключение. Можно бросать любое значение, но на практике используют Error и наследников — у них есть message и stack:

function applyCoupon(cart, code) {
  if (!cart.items.length) {
    throw new Error('Корзина пуста')
  }
  if (code !== 'JSFLOW10') {
    throw new Error('Промокод не найден')
  }
  return cart.total * 0.9
}

Не перехваченное исключение «всплывает» по стеку вызовов, пока не найдётся подходящий catch или процесс не завершится с ошибкой.


Объект Error #

Базовый конструктор:

const err = new Error('Сеть недоступна')
err.name // "Error"
err.message // "Сеть недоступна"
err.stack // строка со стеком вызовов (формат зависит от движка)

Встроенные подтипы (неполный список):

КлассТипичный случай
SyntaxErrorневерный синтаксис при eval, JSON.parse
TypeErrorвызов не-функции, чтение свойства у null
RangeErrorзначение вне допустимого диапазона
ReferenceErrorобращение к необъявленной переменной
JSON.parse('{ broken') // SyntaxError
null.trim() // TypeError

Пользовательские ошибки #

Для различения сценариев в catch удобны собственные классы:

class CheckoutError extends Error {
  constructor(message, code) {
    super(message)
    this.name = 'CheckoutError'
    this.code = code
  }
}

function reserveStock(sku, qty) {
  if (qty > 50) {
    throw new CheckoutError('Превышен лимит на позицию', 'STOCK_LIMIT')
  }
}

try {
  reserveStock('hoodie-xl', 60)
} catch (err) {
  if (err instanceof CheckoutError && err.code === 'STOCK_LIMIT') {
    // показать пользователю понятное сообщение
  } else {
    throw err // неизвестная ошибка — пробрасываем дальше
  }
}

Наследование от Error сохраняет stack и совместимость с instanceof. Поле code (или cause в современных движках) помогает маршрутизировать обработку без разбора текста message.


Ошибки и асинхронный код #

try/catch вокруг колбэка не перехватит ошибку, выброшенную внутри колбэка, вызванного позже:

function loadPrice(sku, onDone) {
  setTimeout(() => {
    throw new Error('Таймаут каталога') // не попадёт в внешний try/catch
  }, 0)
}

try {
  loadPrice('mug-01', () => {})
} catch (err) {
  // сюда не дойдёт
}

Для отложенного кода ошибки передают через колбэк (onError), промис (.catch) или async/await — эти темы в следующих статьях раздела.


Итог #

  • try/catch/finally обрабатывает синхронные исключения в одном стеке вызовов.
  • throw прерывает выполнение; предпочтительно бросать Error и подклассы.
  • Пользовательские ошибки упрощают ветвление в catch по типу или коду.
  • Асинхронные ошибки требуют отдельного канала доставки — не полагайтесь на внешний try/catch вокруг отложенного вызова.

См. также #

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

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