81. Обработка ошибок
JavaScript: Обработка Ошибок
Заголовок раздела «JavaScript: Обработка Ошибок»
В мире разработки редко что-то идет идеально. Ошибки — это неотъемлемая часть любого приложения. Эффективная обработка ошибок критически важна для создания надежного, стабильного и приятного в использовании продукта. Она позволяет вашему приложению “грациозно” реагировать на непредвиденные ситуации, предотвращая крахи и предоставляя пользователю полезную обратную связь.
В JavaScript мы используем конструкции try...catch...finally и механизмы работы с промисами для управления ошибками.
Основы Обработки Ошибок: try...catch
Заголовок раздела «Основы Обработки Ошибок: try...catch»Блок try...catch — это фундамент обработки синхронных ошибок.
// Пример 1: Базовый try...catchfunction divide(a, b) { try { if (b === 0) { throw new Error("Деление на ноль недопустимо!"); // Генерируем ошибку } return a / b; } catch (error) { console.error("Произошла ошибка при делении:", error.message); return null; // Возвращаем безопасное значение или перебрасываем ошибку }}
console.log(divide(10, 2)); // 5console.log(divide(10, 0)); // Произошла ошибка при делении: Деление на ноль недопустимо! (и null)Кастомные Ошибки
Заголовок раздела «Кастомные Ошибки»Для более детальной обработки можно создавать собственные типы ошибок, наследуясь от встроенного Error.
// Пример 2: Кастомная ошибкаclass ValidationError extends Error { constructor(message, field) { super(message); // Вызываем конструктор базового Error this.name = "ValidationError"; // Устанавливаем имя ошибки this.field = field; // Добавляем специфичные данные }}
function validateInput(username) { if (username.length < 3) { throw new ValidationError("Имя пользователя слишком короткое", "username"); } return true;}
try { validateInput("ab");} catch (error) { if (error instanceof ValidationError) { console.error(`Ошибка валидации поля ${error.field}: ${error.message}`); } else { console.error("Неизвестная ошибка:", error); }}// Ошибка валидации поля username: Имя пользователя слишком короткоеБлок finally
Заголовок раздела «Блок finally»Блок finally всегда выполняется, независимо от того, была ли ошибка выброшена или нет. Он идеально подходит для очистки ресурсов.
// Пример 3: Использование finallyfunction processFile(filename) { let fileHandle; try { // Имитация открытия файла if (!filename) throw new Error("Имя файла не указано!"); fileHandle = `open(${filename})`; console.log(`${fileHandle} открыт.`);
// Имитация обработки, которая может вызвать ошибку if (filename === "broken.txt") { throw new Error("Ошибка чтения файла!"); } console.log("Файл успешно обработан."); return true; } catch (error) { console.error("Ошибка при обработке файла:", error.message); return false; } finally { if (fileHandle) { console.log(`${fileHandle} закрыт.`); // Всегда закрываем ресурс } }}
processFile("data.txt");// open(data.txt) открыт.// Файл успешно обработан.// open(data.txt) закрыт.
processFile("broken.txt");// open(broken.txt) открыт.// Ошибка при обработке файла: Ошибка чтения файла!// open(broken.txt) закрыт.Обработка Ошибок в Асинхронном Коде (Промисы)
Заголовок раздела «Обработка Ошибок в Асинхронном Коде (Промисы)»Для промисов используем метод .catch() или try...catch с async/await.
// Пример 4: Обработка ошибок в Промисах (.catch())function fetchData(url) { return new Promise((resolve, reject) => { if (!url.startsWith("http")) { return reject(new Error("Некорректный URL!")); } setTimeout(() => { // Имитация сетевого запроса if (url === "http://bad-api.com") { return reject(new Error("Ошибка сервера 500!")); } resolve(`Данные с ${url}`); }, 100); });}
fetchData("http://good-api.com") .then(data => console.log(data)) .catch(error => console.error("Ошибка Promise:", error.message));// Данные с http://good-api.com
fetchData("ftp://bad-url.com") .then(data => console.log(data)) .catch(error => console.error("Ошибка Promise:", error.message));// Ошибка Promise: Некорректный URL!Обработка Ошибок с async/await
Заголовок раздела «Обработка Ошибок с async/await»async/await позволяет использовать привычный try...catch для асинхронного кода, делая его более читаемым.
// Пример 5: try...catch с async/awaitasync function getAndProcessData(url) { try { const data = await fetchData(url); // await может вызвать ошибку console.log("Полученные данные:", data); // Дополнительная обработка данных } catch (error) { console.error("Ошибка в async функции:", error.message); // Здесь можно показать сообщение пользователю или отправить лог }}
getAndProcessData("http://valid-api.com");// Полученные данные: Данные с http://valid-api.com
getAndProcessData("http://bad-api.com");// Ошибка в async функции: Ошибка сервера 500!Распространение Ошибок (Re-throwing)
Заголовок раздела «Распространение Ошибок (Re-throwing)»Иногда нужно поймать ошибку, что-то с ней сделать (например, залогировать), а затем перебросить ее дальше, чтобы вышестоящий код тоже мог ее обработать.
// Пример 6: Переброс ошибкиfunction processPayment(amount) { try { if (amount <= 0) { throw new Error("Сумма платежа должна быть положительной."); } console.log(`Платеж ${amount} успешно обработан.`); return true; } catch (error) { console.error("Лог: Проблема с платежом:", error.message); throw error; // Перебрасываем ошибку дальше }}
function handleTransaction(orderId, paymentAmount) { try { processPayment(paymentAmount); console.log(`Транзакция ${orderId} завершена.`); } catch (error) { console.error(`Ошибка при выполнении транзакции ${orderId}: ${error.message}`); }}
handleTransaction("ORDER-001", 100);// Платеж 100 успешно обработан.// Транзакция ORDER-001 завершена.
handleTransaction("ORDER-002", -50);// Лог: Проблема с платежом: Сумма платежа должна быть положительной.// Ошибка при выполнении транзакции ORDER-002: Сумма платежа должна быть положительной.Продвинутое Использование и Глобальная Обработка
Заголовок раздела «Продвинутое Использование и Глобальная Обработка»Для более сложных приложений полезно настроить глобальные обработчики.
window.onerror: Перехватывает необработанные ошибки синхронного кода в браузере.window.onunhandledrejection: Перехватывает необработанные ошибки промисов в браузере.process.on('uncaughtException')иprocess.on('unhandledRejection'): Аналогичные механизмы в Node.js.
// Пример 7: Глобальный обработчик (браузер)window.onerror = function(message, source, lineno, colno, error) { console.error("Глобальная ошибка (синхронная):", { message, source, lineno, colno, error }); // Здесь можно отправить лог на сервер return true; // Возвращаем true, чтобы предотвратить стандартное поведение браузера (например, вывод в консоль)};
window.onunhandledrejection = function(event) { console.error("Глобальная ошибка (Promise):", event.reason); // Здесь также можно отправить лог на сервер};
// Пример для демонстрации (вызовет глобальные обработчики)// setTimeout(() => { throw new Error("Тестовая синхронная ошибка!"); }, 100);// Promise.reject("Тестовый отклонённый Promise!");Типичные Ошибки и Как Их Избежать
Заголовок раздела «Типичные Ошибки и Как Их Избежать»- “Проглатывание” ошибок: Пустой блок
catchилиcatchбез логирования – худшая практика. Вы теряете информацию о проблеме.// Плохо:try {//...} catch (error) {// Ничего не делаем, ошибка исчезает} - Неправильная обработка асинхронных ошибок:
try...catchне будет работать напрямую с промисами, если вы не используетеawait.// Не сработает для Promise, если нет await:try {myPromiseFunction(); // Промис выполнится асинхронно} catch (error) {// Эта ошибка не будет поймана, т.к. Promise не был завершен к моменту выхода из try} - Слишком общая обработка: Попытка поймать все возможные ошибки одним
try...catchможет скрыть реальные проблемы. Используйте кастомные ошибки или условную логику вcatch.
Практика и Советы
Заголовок раздела «Практика и Советы»- Логируйте ошибки: Всегда выводите ошибки в консоль или, лучше, отправляйте их в систему мониторинга (например, Sentry, Bugsnag).
- Сообщайте пользователю: Предоставляйте понятные, дружелюбные сообщения об ошибках, вместо технических стектрейсов.
- Используйте
try...catchцеленаправленно: Не оборачивайте каждый чих вtry...catch. Используйте его там, где ожидаете возможные сбои или где нужно восстановиться после ошибки. - Тестируйте обработку ошибок: Убедитесь, что ваши обработчики действительно работают, когда происходят ошибки.
- Используйте
finallyдля очистки: Если у вас есть ресурсы (файлы, сетевые соединения, таймеры), которые нужно закрыть,finally– лучшее место для этого.
Правильная обработка ошибок — это признак профессионального и надежного кода. Освоив эти концепции, вы сможете создавать более устойчивые и удобные приложения.
Практика
Заголовок раздела «Практика»Попробуйте примеры в интерактивном редакторе: