JS обработка ошибок
Обработка ошибок в JavaScript — механизм перехвата и управления исключениями с помощью конструкций
try...catch...finally, объектаErrorи его подклассов.
Зачем нужно
Без обработки ошибок любое исключение прерывает выполнение программы и ломает пользовательский интерфейс. Правильная обработка позволяет graceful degradation — показать понятное сообщение, откатить операцию, залогировать проблему. В асинхронном коде (Promise, async/await) необработанные ошибки могут теряться незаметно, что делает понимание этого механизма критически важным.
Где используется
- Обработка ответов API: сетевые ошибки, статусы 4xx/5xx
- Валидация входных данных: бросать ошибки при некорректном вводе
- Работа с localStorage, IndexedDB, File API — могут выбросить SecurityError
- Асинхронные операции:
.catchв Promise-цепочках,try/catchв async-функциях - Глобальный перехват:
window.onerror,window.onunhandledrejectionдля мониторинга
Основной контент
try...catch...finally
function parseJSON(str) {
try {
return JSON.parse(str); // может выбросить SyntaxError
} catch (err) {
console.error('Ошибка парсинга:', err.message);
return null;
} finally {
console.log('parseJSON завершён'); // выполняется всегда
}
}
parseJSON('{"name":"Иван"}'); // успех
parseJSON('не JSON'); // ошибка перехвачена, возвращает null
Объект Error и его свойства
try {
null.property; // TypeError
} catch (err) {
console.log(err instanceof TypeError); // true
console.log(err.name); // "TypeError"
console.log(err.message); // "Cannot read properties of null"
console.log(err.stack); // стек вызовов
}
// Встроенные подтипы Error
// TypeError — неверный тип
// RangeError — значение вне допустимого диапазона
// ReferenceError — обращение к необъявленной переменной
// SyntaxError — ошибка синтаксиса
// URIError — некорректный URI
Пользовательские ошибки
class ValidationError extends Error {
constructor(message, field) {
super(message);
this.name = 'ValidationError';
this.field = field;
}
}
class NetworkError extends Error {
constructor(message, statusCode) {
super(message);
this.name = 'NetworkError';
this.statusCode = statusCode;
}
}
function validateAge(age) {
if (typeof age !== 'number') {
throw new ValidationError('Возраст должен быть числом', 'age');
}
if (age < 0 || age > 150) {
throw new ValidationError('Недопустимый возраст', 'age');
}
return age;
}
try {
validateAge('abc');
} catch (err) {
if (err instanceof ValidationError) {
console.log(`Ошибка поля ${err.field}: ${err.message}`);
} else {
throw err; // перебрасываем неизвестные ошибки
}
}
Ошибки в асинхронном коде
// Promise — .catch
fetch('/api/data')
.then(res => {
if (!res.ok) throw new NetworkError('Ошибка сети', res.status);
return res.json();
})
.catch(err => {
console.error('Запрос провалился:', err.message);
});
// async/await — try/catch
async function loadUser(id) {
try {
const res = await fetch(`/api/users/${id}`);
if (!res.ok) throw new NetworkError('Не найден', res.status);
return await res.json();
} catch (err) {
if (err instanceof NetworkError && err.statusCode === 404) {
return null; // Обработали 404
}
throw err; // остальное пробрасываем
}
}
Глобальный перехват
// Синхронные ошибки
window.onerror = (message, source, lineno, colno, error) => {
console.error('Глобальная ошибка:', message, error);
return true; // предотвращает вывод в консоль браузера
};
// Необработанные Promise-отказы
window.addEventListener('unhandledrejection', (event) => {
console.error('Unhandled rejection:', event.reason);
event.preventDefault();
});
Частые ошибки
- Проглатывание ошибок: пустой
catchбез логирования скрывает проблемы навсегда. - Catch ловит всё: если вы ловите
Error, но обрабатываете только один тип — перебрасывайте остальные черезif (!(err instanceof MyError)) throw err. - Потеря ошибки в async:
async function foo() { somePromise; }— промис безawaitне перехватываетсяtry/catchвнутри функции. finallyвозвращает значение:returnвfinallyперекрываетreturnвtry— редко нужно, всегда удивляет.