Нова помилка всередині catch
Помилка, кинута всередині catch, не обробляється цим самим catch. Вона піднімається вгору по стеку викликів до найближчого зовнішнього try...catch, а якщо такого немає, завершує програму.
Теорія
TL;DR
catchловить лише те, що сталося в його власному блоціtry.- Помилка з тіла
catchіде вгору по стеку до наступного обробника. - Якщо зовнішнього обробника немає, отримуємо uncaught помилку і падіння програми.
finallyвиконується завжди, навіть колиcatchкинув нову помилку.- Щоб обробити помилку локально, обгорніть тіло
catchу власнийtry...catch.
Швидкий приклад
try {
throw new Error("failed in try");
} catch (err) {
console.log("caught:", err.message);
// a new error right here
throw new Error("failed inside catch");
}Вивід:
caught: failed in try
Uncaught Error: failed inside catchПершу помилку було спіймано, друга лишилася без обробника і поклала програму.
Що відбувається крок за кроком
- У
tryвиникає помилка, керування переходить доcatch. - У
catchпочинає виконуватися код обробки. - Якщо всередині
catchзнову виникає помилка, поточнийcatchїї вже не ловить: він відповідає тільки за помилки зі свогоtry. - Нова помилка піднімається вгору по стеку, до найближчого зовнішнього
try...catch, якщо він є. - Якщо зовнішнього
try...catchнемає, програма завершується з помилкою.
Зовнішній try...catch ловить помилку з catch
Щоб обробити нову помилку, потрібна ще одна конструкція рівнем вище:
try {
try {
throw new Error("failed in try");
} catch (err) {
console.log("inner catch:", err.message);
throw new Error("failed in catch");
}
} catch (err) {
console.log("outer catch caught:", err.message);
}Вивід:
inner catch: failed in try
outer catch caught: failed in catchТепер зовнішня конструкція спіймала помилку, яка сталася всередині блоку catch.
А ось що буде без зовнішнього обробника, помилка просто валить скрипт:
try {
throw new Error("first failure");
} catch (e) {
console.log("catch:", e.message);
JSON.parse("invalid json"); // SyntaxError
}Вивід:
catch: first failure
Uncaught SyntaxError: Unexpected token 'i', "invalid json" is not valid JSONfinally виконується в будь-якому разі
Навіть якщо в catch виникла нова помилка, блок finally все одно відпрацює, перш ніж помилка спливе назовні.
try {
try {
throw new Error("failed in try");
} catch (e) {
console.log("catch:", e.message);
throw new Error("failed in catch");
} finally {
console.log("finally: always runs");
}
} catch (e) {
console.log("outer catch:", e.message);
}Вивід:
catch: failed in try
finally: always runs
outer catch: failed in catchПорядок важливий: спершу finally, і лише потім помилка потрапляє до зовнішнього catch.
Вкладений try усередині catch
Ще один спосіб, обгорнути тіло catch у власний try...catch:
try {
throw new Error("failed in try");
} catch (e) {
try {
console.log("catch:", e.message);
throw new Error("failed inside catch");
} catch (innerErr) {
console.log("caught the inner one:", innerErr.message);
}
}Вивід:
catch: failed in try
caught the inner one: failed inside catchТак помилки всередині catch теж лишаються під контролем і нікуди не спливають.
Підсумкова таблиця
| Сценарій | Що відбувається |
|---|---|
Помилка в catch | Поточний catch її не ловить |
Є зовнішній try...catch | Помилка передається туди |
Немає зовнішнього catch | Програма падає |
Є finally | Він усе одно виконується |
Тіло catch обгорнуте у свій try | Внутрішня помилка ловиться локально |
Типові помилки
- Вважати, що
catchперехопить власнийthrow. Обробник уже активний і сам себе не ловить. - Забувати, що після
throwуcatchрешта тілаcatchне виконається. - Думати, що нова помилка в
catchскасовуєfinally.finallyвиконується завжди. - Кидати нову помилку і губити початкову причину. Передавайте її далі:
throw new Error("wrapper", { cause: err }). - Кидати помилку з
finally. Такийthrowперекриває виняток, що вже летів назовні, і початкова помилка зникає безслідно.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.