Обработка ошибок
try/catch/finally, правильный throw, свои ошибки и когда ничего не ловить.
Обновлено
Что такое ошибка
Ошибка (исключение) — сигнал, что что-то пошло не так и программа не может нормально продолжать в этой точке. Когда она возникает, JS останавливает текущую функцию и «поднимается» по вызовам, пока кто-то её не поймает. Если никто не поймал → ошибка в консоли, а в React → экран ошибки.
Ошибка — это объект с полями:
name— тип (TypeError,HttpError);message— что случилось;stack— где это случилось (цепочка вызовов) — самая полезная информация при отладке;cause(необязательно) — исходная ошибка, которая её вызвала.
Нативные типы ошибок
| Тип | Когда возникает | Пример |
|---|---|---|
TypeError |
операция над неверным типом | undefined.name, 5() |
ReferenceError |
несуществующая переменная / в TDZ | используете необъявленную x |
SyntaxError |
невалидный код или JSON | JSON.parse('{nope') |
RangeError |
значение вне допустимых пределов | new Array(-1) |
Ожидаемые и неожиданные ошибки
Самое важное различие:
| Ожидаемые | Неожиданные | |
|---|---|---|
| Примеры | неверный email, товара нет в наличии, 404 | баг, упавший сервер, undefined.x |
| Как обрабатывать | возвращаемыми значениями: { ok: false, error } |
исключениями: throw |
| Кто их видит | пользователь, с понятным сообщением | логи / мониторинг; пользователь видит общее сообщение |
Проверка формы — это не «исключительная ситуация»: пользователь ошибается, это нормально. Возвращайте результат, а не бросайте исключение.
try / catch / finally
try {
const data = JSON.parse(raw) // код, который может бросить ошибку
render(data)
} catch (error) {
showToast('Неверные данные') // выполняется, только если возникла ошибка
} finally {
setLoading(false) // выполняется всегда, с ошибкой или без
}throw — как бросать правильно
throw new Error('Пользователь не найден') // ✓ есть stack trace
throw new Error('Запрос не удался', { cause: originalError }) // ✓ сохраняете причину
throw 'что-то' // ✗ ни стека, ни типаСвои ошибки
Когда коду, который ловит, нужно различать типы ошибок:
class HttpError extends Error {
constructor(status, message) {
super(message)
this.name = 'HttpError'
this.status = status
}
}
try {
await api('/users/5')
} catch (e) {
if (e instanceof HttpError && e.status === 404) return notFound()
throw e // что не умеете обработать — бросаете дальше
}Ошибки в асинхронном коде
async function load() {
try {
const res = await fetch('/api')
if (!res.ok) throw new HttpError(res.status, 'Запрос не удался')
return await res.json()
} catch (e) {
console.error(e)
return null
}
}Отклонённый и непойманный Promise = unhandledrejection. Подробнее — в Async.
Где ловить ошибки — правило
- Ловите там, где можете сделать что-то полезное: сообщение пользователю, повтор, запасное значение.
- Не ловите «чтобы не упало» — проглоченная ошибка прячет баг.
- Никогда не пишите пустой
catch {}. - В React / Next:
error.tsx— страховочная сетка для того, что проскочило.
Коротко
- Ошибка = объект с
name,message,stack; поднимается, пока её не поймают. - Ожидаемые ошибки → возвращаемые значения; неожиданные →
throw new Error(...). try/catch/finally; свои ошибки черезextends Error; не глотайте ошибки.