Angularшлях до Strong Junior
Модулі / JavaScript: основа / Помилки: throw і try/catch
🔥 0

Фундамент Ф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 — завжди.
  • Лови там, де можеш відреагувати, і ніколи не ковтай мовчки.

≈ 30 хв · +25 XP за урок