Ошибки — try/catch без боли
Ошибки — это не «когда что-то плохо», а нормальная часть программы. Файл может не открыться, сервер вернёт 500, юзер ввёл фигню. Уметь их ловить и обрабатывать — отдельный навык, который ИИ часто реализует с перекосами (перестраховка ИЛИ полное игнорирование).
throw — кидаем ошибку
throw new Error("сообщение") — выбрасывает ошибку. После throw код функции ОБРЫВАЕТСЯ — то что после, не выполнится. Ошибка «всплывает» вверх по стеку вызовов пока кто-то её не поймает (try/catch). Если до самого верха никто не поймал — программа падает с ошибкой в консоль.
try/catch — ловим
В try-блоке выполняется код, в catch — что делать если случилась ошибка. e (или err) — объект ошибки с полями message (текст), name (тип), stack (где случилось). После catch программа продолжается дальше, не падает. Опционально finally { ... } — выполнится ВСЕГДА, успех или ошибка.
Главное правило: НЕ съедать
Худший паттерн: try { ... } catch {} — поймали и забыли. Ошибка случается, но вы об этом не узнаете, и баг живёт в продакшне неделями. Минимум: console.error(e). Лучше: показать пользователю что-то осмысленное («не удалось загрузить, проверьте интернет»). ИИ часто делает «защитный» try/catch вокруг всего — в большинстве случаев это лишнее.
Когда кидать самому
Когда программа дальше точно не сможет работать осмысленно. Пример: функция считает скидку, а массив товаров пустой —
лучше чем «вернуть 0» (и пользователь увидит «скидка 0% — почему?»). Кидать ошибку — это передать решение «как с этим жить» на уровень выше.
Свои типы ошибок
Иногда хочется отличать «сетевую» ошибку от «валидации». Тогда:
потом ловишь и проверяешь
Это уже продвинуто, но видишь часто в TypeScript-проектах. ИИ любит делать кучу классов ошибок — оцени, нужно ли тебе их столько.
// === БАЗОВЫЙ try/catch ===
async function loadUser(id) {
try {
const r = await fetch(`/api/users/${id}`);
if (!r.ok) throw new Error(`HTTP ${r.status}`);
return await r.json();
} catch (e) {
console.error('Не загрузили пользователя:', e.message);
return null; // явный fallback вместо падения
}
}
// === ПЛОХО — съели ошибку ===
async function badLoad() {
try {
return await fetch('/api/x').then(r => r.json());
} catch {
// молча. Что-то пошло не так — никто не узнает.
}
}
// === ХОРОШО — конкретная обработка по типу ===
async function smartLoad(id) {
try {
const r = await fetch(`/api/users/${id}`);
if (r.status === 404) {
return { notFound: true }; // не ошибка, штатный кейс
}
if (!r.ok) {
throw new Error(`Сервер вернул ${r.status}`);
}
return await r.json();
} catch (e) {
if (e.name === 'TypeError') {
// fetch кидает TypeError при отсутствии сети
return { error: 'Нет интернета' };
}
return { error: e.message };
}
}
// === КОГДА КИДАТЬ САМОМУ ===
function calcDiscount(items, percent) {
if (!items || items.length === 0) {
throw new Error('Нет товаров для расчёта');
}
if (percent < 0 || percent > 100) {
throw new Error('Процент должен быть 0-100');
}
const total = items.reduce((s, i) => s + i.price, 0);
return total * (1 - percent / 100);
}
// Использующий код решает что делать с ошибкой:
try {
const sum = calcDiscount(cart, 15);
showResult(sum);
} catch (e) {
showToast('Не получилось: ' + e.message);
}
// === finally — всегда выполнится ===
async function withSpinner(fn) {
showSpinner(true);
try {
return await fn();
} finally {
showSpinner(false); // спиннер скрыли при успехе И при ошибке
}
}Хорошие и плохие паттерны обработки ошибок