Фундамент Ф1 · урок 10 з 12
Помилки: throw і try/catch
Помилки неминучі: сервер не відповів, користувач увів у поле ціни «abc», дані прийшли не в тій формі. Хороший код відрізняється не тим, що в ньому не буває помилок, а тим, як він із ними поводиться: показує людині зрозуміле повідомлення, не падає цілком і не ховає проблему. Цей урок — про те, як помилки влаштовані й коли їх ловити.
Три види помилок
| Вид | Коли видно | Приклад |
|---|---|---|
| Синтаксична | одразу: код навіть не запуститься | незакрита дужка, зайва кома |
| Виконання | коли дійшли до рядка | TypeError: Cannot read properties of undefined |
| Логічна | ніколи сама — код працює, але неправильно | знижка рахується двічі |
Перші дві JavaScript показує сам — ти вже бачив їх у пісочниці. Третю ловлять лише тести й уважність, тому вона найнебезпечніша.
Як читати помилку
У помилки виконання завжди три частини: тип (TypeError, ReferenceError…),
повідомлення (що саме не так) і місце — файл і рядок. У консолі браузера під нею ще й стек викликів:
список функцій, через які пройшло виконання, від найглибшої до зовнішньої. Читай знизу вгору — побачиш шлях від
свого коду до місця, де впало.
Найчастіші типи: ReferenceError — звернулись до змінної, якої немає; TypeError — зробили з
значенням те, чого з ним не можна (викликали не функцію, взяли властивість у undefined);
SyntaxError — код не вдалось прочитати.
throw: кинути свою помилку
Помилку можна створити й «кинути» самому. Це роблять, коли функція отримала дані, з якими вона чесно не може працювати, — краще голосно впасти одразу, ніж тихо повернути нісенітницю.
function parsePrice(text) {
const n = Number(text);
if (Number.isNaN(n)) {
throw new Error(`Некоректна ціна: ${text}`); // виконання функції тут зупиняється
}
return n;
}
throw перериває функцію, як return, але замість значення в того, хто викликав, «прилітає»
помилка. Якщо її ніхто не впіймає, вона піднімається далі, аж до самого верху — і програма падає.
try / catch / finally: впіймати
try {
const price = parsePrice(input); // пробуємо
save(price);
} catch (error) {
showMessage(error.message); // сюди потрапляємо, лише якщо в try щось кинуло помилку
} finally {
loading = false; // виконається за будь-якого результату
}
Якщо всередині try щось кинуло помилку, решта try пропускається і виконується
catch — з обʼєктом помилки в змінній. finally виконується завжди: і після успіху, і після
помилки. Туди кладуть «прибирання» — наприклад, вимкнути індикатор завантаження.
Допиши parsePrice: кидай помилку Некоректна ціна: …, якщо вийшло не число
або число менше нуля. Цикл нижче вже вміє ловити помилки й виводити повідомлення.
if (Number.isNaN(n) || n < 0) { throw new Error(`Некоректна ціна: ${text}`); }Де ловити, а де ні
Головне правило: лови там, де можеш щось зробити. Показати повідомлення, повторити запит, підставити
значення за замовчуванням — це привід для catch. А обгортати кожен рядок «про всяк випадок» — шкідливо.
// ✗ найгірший catch: помилку проковтнули, і ніхто не дізнається, що щось зламалось
try {
saveSettings(data);
} catch (e) {}
// ✓ або обробити по-справжньому, або хоча б не мовчати
try {
saveSettings(data);
} catch (e) {
console.error('Не вдалось зберегти налаштування', e);
showMessage('Налаштування не збереглись. Спробуй ще раз.');
}
Свої типи помилок
Помилка — звичайний обʼєкт класу Error з полями name, message і
stack. Від нього можна наслідуватись (урок 8), щоб розрізняти помилки в catch:
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = 'ValidationError';
}
}
try {
throw new ValidationError('Назва порожня');
} catch (e) {
if (e instanceof ValidationError) {
showFieldError(e.message); // очікувана помилка — показали біля поля
} else {
throw e; // неочікувану — кидаємо далі, не ковтаємо
}
}
Подивись, у якому порядку виконуються try, catch і finally.
Потім заміни 'abc' на '100' і запусти ще раз.
Асинхронні помилки — попередження
try/catch ловить лише те, що сталося поки він виконується. Якщо помилка станеться пізніше —
у відкладеній функції чи у відповіді від сервера, — звичайний try її вже не впіймає. Для цього є
.catch у Promise і try/catch разом з await (Ф2), а в Angular — оператор
catchError з RxJS (модуль 4, урок про помилки).
- Порожній
catch {}— помилку проковтнули, проблема лишилась, але її вже не видно. throw 'текст'замістьthrow new Error('текст')— у рядка немаєstack, важче шукати.- Очікування, що
tryвпіймає помилку зsetTimeoutчи запиту — не впіймає. - Ловлять помилку «на всяк випадок» глибоко всередині, де з нею нічого не зробиш, — лови там, де можеш відреагувати.
Підсумок
- Помилка має тип, повідомлення й місце. Стек читай знизу вгору — від свого коду до місця падіння.
throw new Error('…')— коли з даними чесно не можна працювати.try/catch/finally:catchлише при помилці,finally— завжди.- Лови там, де можеш відреагувати, і ніколи не ковтай мовчки.