Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Помилки та їх обробка». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Синхронні помилки ловлять блоком `try/catch/finally`, а створюють через `throw new Error(...)`; асинхронні ловлять або тим самим `try/catch` навколо `await`, або методом `.catch()` на промісі.** Для доменних випадків роблять власні класи, що успадковують `Error`, і передають первинну помилку через `{ cause }`. Останньою лінією оборони є глобальні перехоплювачі: `error` і `unhandledrejection` у браузері, `uncaughtException` і `unhandledRejection` у Node.js. ```javascript try { risky(); } catch (err) { console.error(err.message); } finally { cleanup(); // виконується завжди } ``` **Ключове:** обробляйте помилку локально там, де ви можете щось із нею зробити, а глобальні хендлери тримайте як страховку для логування, а не як заміну локальної обробки.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**Обробка помилок у JavaScript будується на трьох рівнях: локальний `try/catch/finally` навколо ризикованого коду, обробка відхилених промісів в асинхронних операціях і глобальні перехоплювачі як остання лінія оборони.** Помилку створюють оператором `throw`, а для доменних сценаріїв описують власні класи, що успадковують `Error`. ## Теорія ### TL;DR - `try/catch/finally` ловить синхронні винятки; `finally` виконується завжди. - Помилку кидають через `throw new Error('...')`, і кидати варто саме об'єкт `Error`, а не рядок. - Власні класи (`class ValidationError extends Error`) дають типізовані доменні помилки; `{ cause }` зберігає первинну причину. - В `async/await` працює звичайний `try/catch`; у ланцюжках промісів: `.catch()`. - `Promise.allSettled` не валить увесь батч через одну невдалу операцію. - Глобальні хендлери: `window.addEventListener('error' | 'unhandledrejection')` у браузері, `process.on('uncaughtException' | 'unhandledRejection')` у Node.js. ### Швидкий приклад ```javascript try { risky(); // код, який може впасти } catch (err) { console.error(err.message); // обробка або логування } finally { cleanup(); // виконується завжди (навіть при помилці) } ``` Генерація помилки: ```javascript if (!user) { throw new Error('User not found'); } ``` ### Власні класи помилок Успадкування від `Error` дає можливість відрізняти доменні помилки від технічних і обробляти їх вибірково через `instanceof`. ```javascript class ValidationError extends Error { constructor(message, cause) { super(message, { cause }); // Node 16+ і сучасні браузери: err.cause this.name = 'ValidationError'; } } throw new ValidationError('Invalid email'); ``` Поле `cause` зберігає первинну помилку, тому низькорівневу причину не втрачено, коли ви загортаєте її у зрозумілу користувачеві. ### Асинхронний код **a) `async/await` разом із `try/catch`** ```javascript async function load() { try { const res = await fetch('/api/data'); if (!res.ok) throw new Error(`HTTP ${res.status}`); return await res.json(); } catch (err) { // обробка, повторна спроба або сповіщення console.error('Load failed:', err); return null; // або пробросити далі: throw err } } ``` Зверніть увагу: `fetch` не кидає помилку на HTTP-статус 404 чи 500, тому статус перевіряють вручну через `res.ok`. **b) Проміси: `.catch()`** ```javascript fetch('/api/data') .then(r => r.json()) .catch(err => console.error('Request failed', err)); ``` **c) Паралельні операції без падіння всього батча** ```javascript const results = await Promise.allSettled(urls.map(u => fetch(u))); for (const r of results) { if (r.status === 'fulfilled') console.log('OK', r.value); else console.warn('Failed', r.reason); } ``` `Promise.all` відхиляється на першій же невдачі, а `Promise.allSettled` дочікується всіх і повертає статус кожної операції окремо. ### Глобальні перехоплювачі (остання лінія оборони) **Браузер** ```javascript // синхронні та непіймані винятки window.addEventListener('error', (event) => { console.error('Uncaught error:', event.error, event.message, event.filename, event.lineno); }); // непіймані відхилення промісів window.addEventListener('unhandledrejection', (event) => { console.error('Unhandled rejection:', event.reason); }); ``` **Node.js** ```javascript process.on('uncaughtException', (err) => { console.error('Uncaught Exception', err); // логування та коректне завершення процесу process.exit(1); }); process.on('unhandledRejection', (reason) => { console.error('Unhandled Rejection', reason); // рішення залежить від політики застосунку: логувати і завершувати чи продовжувати }); ``` > Глобальні хендлери, це страховка, а не заміна локальної обробки. Після `uncaughtException` процес перебуває в невизначеному стані, тому рекомендована практика: залогувати, коректно закрити ресурси і завершити процес, доручивши перезапуск менеджеру процесів. ### Типові помилки - **Порожній `catch`.** Блок, що мовчки ковтає виняток, перетворює баг на невидиму поведінку; щонайменше логуйте. - **`throw 'текст'` замість `throw new Error('текст')`.** Рядок не має ані `stack`, ані `name`, тому стек-трейс втрачено. - **Забутий `await` перед викликом усередині `try`.** Проміс відхилиться вже після виходу з блока, і `catch` його не побачить. - **Покладатися на те, що `fetch` кине помилку на 500.** Він відхиляється лише на мережевій помилці, статус перевіряють через `res.ok`. - **`try/catch` навколо асинхронного колбека.** Виняток усередині `setTimeout(() => { ... })` вилітає в інший такт event loop і зовнішнім блоком не ловиться. - **Глобальний хендлер замість локального.** Він годиться для телеметрії, але не дає користувачеві осмисленого повідомлення і не відновлює стан застосунку.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.