Skip to main content

Помилки всередині функцій

Помилки всередині функції обробляють через try...catch, а в асинхронному коді через той самий try...catch навколо await або через .catch() у промісах. Задача обробки не в тому, щоб приховати збій, а в тому, щоб функція поводилася передбачувано: повернула безпечне значення або прокинула зрозумілу помилку вище.

Теорія

TL;DR

  • try...catch захищає синхронний код: усе, що в try, виконується «під захистом», а при помилці керування переходить у catch.
  • В async функціях try...catch ставлять усередині, навколо await.
  • Без await помилки промісу ловить .catch() у ланцюжку.
  • Помилку можна не тільки зловити, а й прокинути вище через throw new Error(...).
  • Використовуйте try...catch точково, там, де ви справді очікуєте збій.
  • Не глушіть помилки без логування і повертайте передбачуване значення при збої.

Швидкий приклад

javascript
function divide(a, b) { try { if (b === 0) { throw new Error('Division by zero is not possible'); } return a / b; } catch (error) { console.error('Error:', error.message); return null; // safe fallback value } } console.log(divide(10, 0)); // Error: Division by zero is not possible -> null

Що тут відбувається:

  • усе, що всередині try, виконується «під захистом»;
  • якщо виникає помилка, виконання переходить у catch;
  • після цього програма не падає, а функція повертає передбачуване null.

Синхронна обробка помилок

try...catch використовують тоді, коли помилка може статися в момент виконання функції: некоректний аргумент, невдалий розбір рядка, звернення до властивості null. Блок catch отримує об'єкт помилки, у якого є щонайменше name, message і stack.

Важливо розуміти межі: try...catch ловить лише те, що кинуто синхронно всередині блока. Помилка з колбека, який виконається пізніше (наприклад, у setTimeout), уже не потрапить у цей catch, бо на момент її виникнення блок давно завершився.

Асинхронні помилки в async/await

Для async функцій try...catch ставлять усередині асинхронного блока, навколо await. Тоді відхилений проміс перетворюється на звичайний виняток, який ловиться на місці:

javascript
async function fetchUser(id) { try { const res = await fetch(`/api/users/${id}`); if (!res.ok) throw new Error('User not found'); return await res.json(); } catch (error) { console.error('Request failed:', error.message); return null; } }

Зверніть увагу на перевірку res.ok: fetch відхиляє проміс лише при мережевій помилці, а відповідь зі статусом 404 або 500 для нього цілком успішна. Тож помилку HTTP треба кидати вручну.

Помилки в промісах

Якщо await не використовується, помилки ловлять через .catch() наприкінці ланцюжка. Один .catch() покриває всі попередні .then():

javascript
fetch('/api/data') .then(res => res.json()) .then(data => console.log(data)) .catch(err => console.error('Error:', err));

Ланцюжок без .catch() дає необроблене відхилення (unhandled rejection), яке в Node.js може завершити процес.

Створення і проброс помилок вручну

Іноді помилку треба не тільки зловити, а й передати вище, додавши контекст. Тоді в catch кидають нову помилку:

javascript
function parseJSON(str) { try { return JSON.parse(str); } catch (error) { throw new Error('Invalid JSON: ' + error.message); } } try { parseJSON('{invalid}'); } catch (e) { console.error('Caught at the top level:', e.message); }

Так низькорівнева помилка розбору перетворюється на зрозуміле повідомлення, а рішення, що робити далі, ухвалює код, який викликав функцію.

Добрі практики

  • Використовуйте try...catch точково, там, де реально очікуєте помилку, а не навколо всього тіла функції.
  • Не глушіть помилки без логування: порожній catch це найшвидший спосіб втратити день на дебаг.
  • Повертайте при збої передбачуване значення: null, {}, false, і документуйте це.
  • Для серверних функцій логуйте через logger, Sentry або хоча б console.error.
  • Для чистих функцій краще кидати помилку, а не логувати: нехай рішення ухвалює код, що викликав.

Типові помилки

  • Порожній catch. catch (e) {} робить збій невидимим, а програму, яка «просто нічого не робить», неможливо діагностувати.
  • try...catch навколо асинхронного колбека. Обгортання setTimeout або старого колбека у try не ловить нічого: помилка станеться після виходу з блока.
  • Забути await перед промісом усередині try. Без await функція поверне проміс, а відхилення пролетить повз catch.
  • Покладатися на те, що fetch сам кине помилку на 404. Він відхиляється лише при мережевому збої, тож статус треба перевіряти вручну.
  • Кидати рядок замість Error. throw 'oops' позбавляє вас стеку викликів і ламає код, який очікує error.message.
  • Ловити помилку там, де ви не можете нічого з нею зробити. Якщо функція не вміє відновитися, чесніше прокинути помилку вище, ніж повернути тихий null.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.