Помилки всередині функцій
Помилки всередині функції обробляють через try...catch, а в асинхронному коді через той самий try...catch навколо await або через .catch() у промісах. Задача обробки не в тому, щоб приховати збій, а в тому, щоб функція поводилася передбачувано: повернула безпечне значення або прокинула зрозумілу помилку вище.
Теорія
TL;DR
try...catchзахищає синхронний код: усе, що вtry, виконується «під захистом», а при помилці керування переходить уcatch.- В
asyncфункціяхtry...catchставлять усередині, навколоawait. - Без
awaitпомилки промісу ловить.catch()у ланцюжку. - Помилку можна не тільки зловити, а й прокинути вище через
throw new Error(...). - Використовуйте
try...catchточково, там, де ви справді очікуєте збій. - Не глушіть помилки без логування і повертайте передбачуване значення при збої.
Швидкий приклад
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. Тоді відхилений проміс перетворюється на звичайний виняток, який ловиться на місці:
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():
fetch('/api/data')
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.error('Error:', err));Ланцюжок без .catch() дає необроблене відхилення (unhandled rejection), яке в Node.js може завершити процес.
Створення і проброс помилок вручну
Іноді помилку треба не тільки зловити, а й передати вище, додавши контекст. Тоді в catch кидають нову помилку:
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.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.