try...catch
Інструкція try...catch
(спробувати-перехопити) складається з блока try
і або блока catch
, або блока finally
, або їх обох. Код в блоку try
виконується першим, і якщо він викидає виняток, то виконується код у блоку catch
. Код у блоку finally
виконується в будь-якому випадку, перед тим, як контроль виконання полишає всю конструкцію.
Спробуйте його в дії
Синтаксис
try {
tryStatements
} catch (exceptionVar) {
catchStatements
} finally {
finallyStatements
}
tryStatements
Інструкції до виконання.
catchStatements
Інструкція, що виконується, коли блок
try
викидає виняток.exceptionVar
Необов'язковеНеобов'язковий ідентифікатор або патерн, що містить перехоплений виняток для відповідного блока
catch
. Якщо блокcatch
не використовує значення винятку, то можна опуститиexceptionVar
і дужки біля нього.finallyStatements
Інструкції, що виконуються перед тим, як контроль виконання виходить з конструкції
try...catch...finally
. Ці інструкції виконуються незалежно від того, чи був викинутий або перехоплений виняток.
Опис
Інструкція try
завжди починається з блока try
. Далі має бути блок catch
або блок finally
. Крім того, може бути як блок catch
, так і блок finally
разом. Відтак – три варіанти інструкції try
:
try...catch
try...finally
try...catch...finally
На відміну від інших конструкцій, як то if
чи for
, блоки try
, catch
і finally
мусять бути блоками, а не одиничними інструкціями.
try doSomething(); // SyntaxError
catch (e) console.log(e);
Блок catch
містить інструкції, котрі вказують, що робити, якщо блок try
викинув виняток. Якщо будь-яка інструкція всередині блока try
(або в функції, викликаної зсередини блока try
) викидає виняток, контроль негайно переходить до блока catch
. Якщо в блоку try
не викидається жодний виняток, то блок catch
пропускається.
Блок finally
обов'язково буде виконаний перед виходом контролю виконання з конструкції try...catch...finally
. Він завжди виконується, незалежно від того, чи було викинуто або перехоплено виняток.
Інструкції try
можна вкладати одну в одну. Якщо внутрішня інструкція try
не має блока catch
, то використовується блок catch
зовнішньої try
.
Також інструкцію try
можна застосовувати для обробки винятків JavaScript. Дивіться подробиці щодо винятків JavaScript у Посібнику з JavaScript.
Зв'язування catch
Коли в блоку try
викидається виняток, то exceptionVar
(тобто e
в catch (e)
) містить значення цього винятку. Це зв'язування можна використовувати для отримання інформації про викинутий виняток. Воно доступне лише в області видимості блоку catch
.
Це не обов'язково повинен бути один ідентифікатор. Для присвоєння кількох ідентифікаторів за раз можна використовувати патерн деструктурування.
try {
throw new TypeError("ой");
} catch ({ name, message }) {
console.log(name); // "TypeError"
console.log(message); // "ой"
}
Зв'язування, створені положенням catch
, живуть в тій же області видимості, що й сам блок catch
, тож всі змінні, оголошені в блоці catch
, не можуть мати назв, що збігаються з назвами зв'язувань, створених положенням catch
. (Існує один виняток з цього правила, але такий запис – нерекомендований.)
try {
throw new TypeError("ой");
} catch ({ name, message }) {
var name; // SyntaxError: Identifier 'name' has already been declared
let message; // SyntaxError: Identifier 'message' has already been declared
}
Зв'язування винятку – доступне для запису. Наприклад, можна нормалізувати значення винятку, аби мати певність, що це об'єкт Error
.
try {
throw "Ой; це не об'єкт Error";
} catch (e) {
if (!(e instanceof Error)) {
e = new Error(e);
}
console.error(e.message);
}
Якщо значення винятку не потрібне, то його можна пропустити вкупі з дужками навколо нього.
function isValidJSON(text) {
try {
JSON.parse(text);
return true;
} catch {
return false;
}
}
Блок finally
Блок finally
містить інструкції, що будуть виконані після виконання блоку try
та блоку (чи блоків) catch
, але до інструкцій, що стоять після блоку try...catch...finally
. Потік виконання обов'язково зайде до блоку finally
, що може відбутися в один з наступних способів:
- Безпосередньо після нормального завершення виконання блоку
try
(винятків викинуто не було); - Безпосередньо після нормального завершення виконання блоку
catch
; - Безпосередньо перед виконанням інструкції контролю потоку виконання (
return
,throw
,break
,continue
) в блокуtry
чи блокуcatch
, що призвело б до виходу з такого блоку.
Якщо виняток викидається з блоку try
, то навіть якщо немає блоку catch
для обробки винятку, блок finally
все одно виконується, у випадку чого виняток все одно викидається негайно після закінчення виконання блоку finally
.
Наступний приклад демонструє одну з ситуацій для використання блоку finally
. Код відкриває файл, а потім виконує інструкції, що використовують цей файл; блок finally
пересвідчується, що файл обов'язково закривається після використання, навіть якщо було викинуто виняток.
openMyFile();
try {
// затягти ресурс
writeMyFile(theData);
} finally {
closeMyFile(); // завжди закривати ресурс
}
Інструкції контролю потоку виконання (return
, throw
, break
, continue
) в блоку finally
будуть "приховувати" будь-яке підсумкове значення блоку try
чи блоку catch
. В прикладі нижче блок try
пробує повернути 1, але перед поверненням контроль виконання спершу переходить до блоку finally
, тож натомість повертається повернене значення блоку finally
.
function doIt() {
try {
return 1;
} finally {
return 2;
}
}
doIt(); // повертає 2
Загалом є недоброю ідеєю мати інструкції контролю потоку виконання в блоку finally
. Його слід використовувати лише для коду очищення.
Приклади
Безумовний блок catch
Коли застосовується блок catch
, то цей блок виконується, коли зсередини блоку try
викидається будь-який виняток. Наприклад, коли в наступному коді трапляється виняток, то контроль передається блокові catch
.
try {
throw "myException"; // породжується виняток
} catch (e) {
// інструкції для обробки будь-яких винятків
logMyErrors(e); // передача об'єкта винятку до обробника помилок
}
Блок catch
задає ідентифікатор (у прикладі вище це e
), що містить значення винятку; це значення доступне лише в області видимості блоку catch
.
Умовні блоки catch
"Умовні блоки catch
" можна створити шляхом поєднання блоків try...catch
зі структурами if...else if...else
, отак:
try {
myRoutine(); // може викидати винятки трьох типів
} catch (e) {
if (e instanceof TypeError) {
// інструкції для обробки винятків TypeError
} else if (e instanceof RangeError) {
// інструкції для обробки винятків RangeError
} else if (e instanceof EvalError) {
// інструкції для обробки винятків EvalError
} else {
// інструкції для обробки всіх непередбачених винятків
logMyErrors(e); // передати об'єкт винятку до обробника помилок
}
}
Поширена ситуація для застосування такого патерну – перехоплення (і заглушення) невеликої підмножини очікуваних помилок, а потім – повторне викидання винятку в інших випадках:
try {
myRoutine();
} catch (e) {
if (e instanceof RangeError) {
// інструкції для обробки цієї дуже поширеної очікуваної помилки
} else {
throw e; // перевикидання помилки без змін
}
}
Це може імітувати синтаксис інших мов, наприклад, Java:
try {
myRoutine();
} catch (RangeError e) {
// інструкції для обробки цієї дуже поширеної очікуваної помилки
}
// Решта помилок неявно перевикидається
Вкладені блоки try
По-перше, погляньмо, що станеться з цим:
try {
try {
throw new Error("йой");
} finally {
console.log("finally");
}
} catch (ex) {
console.error("зовнішній", ex.message);
}
// Друкує:
// "finally"
// "зовнішній" "йой"
Тепер – якщо виняток перехоплений уже внутрішнім блоком try
: додається блок catch
:
try {
try {
throw new Error("йой");
} catch (ex) {
console.error("внутрішній", ex.message);
} finally {
console.log("finally");
}
} catch (ex) {
console.error("зовнішній", ex.message);
}
// Друкує:
// "внутрішній" "йой"
// "finally"
А тепер – повторне викидання помилки.
try {
try {
throw new Error("йой");
} catch (ex) {
console.error("внутрішній", ex.message);
throw ex;
} finally {
console.log("finally");
}
} catch (ex) {
console.error("зовнішній", ex.message);
}
// Друкує:
// "внутрішній" "йой"
// "finally"
// "зовнішній" "йой"
Будь-який виняток буде перехоплений лише раз, найближчим блоком catch
, котрий його охоплює, якщо цей виняток не викидається повторно. Звісно, будь-які нові винятки, винесені у "внутрішньому" блоку (тому, що код в блоку catch
може робити щось, що викидає винятки), будуть перехоплені в "зовнішньому" блоку.
Повернення значення з блоку finally
Якщо блок finally
повертає значення, то таке значення стає поверненим значенням всієї інструкції try-catch-finally
, незалежно від будь-яких інструкцій return
в блоках try
і catch
. Так само це стосується винятків, викинутих зсередини блоку catch
:
(() => {
try {
try {
throw new Error("йой");
} catch (ex) {
console.error("внутрішній", ex.message);
throw ex;
} finally {
console.log("finally");
return;
}
} catch (ex) {
console.error("зовнішній", ex.message);
}
})();
// Друкує:
// "внутрішній" "йой"
// "finally"
Зовнішній "йой" не викидається у зв'язку з return
у блоку finally
. Те саме стосується будь-якого значення, поверненого з блоку catch
.
Специфікації
Сумісність із браузерами
desktop | mobile | server | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
try...catch
|
Chrome Full support 1 | Edge Full support 12 | Firefox Full support 1 | Internet Explorer Full support 5 | Opera Full support 4 | Safari Full support 1 | WebView Android Full support 1 | Chrome Android Full support 18 | Firefox for Android Full support 4 | Opera Android Full support 10.1 | Safari on iOS Full support 1 | Samsung Internet Full support 1.0 | Deno Full support 1.0 | Node.js Full support 0.10.0 |
Optional catch binding
|
Chrome Full support 66 | Edge Full support 79 | Firefox Full support 58 | Internet Explorer No support Ні | Opera Full support 53 | Safari Full support 11.1 | WebView Android Full support 66 | Chrome Android Full support 66 | Firefox for Android Full support 58 | Opera Android Full support 47 | Safari on iOS Full support 11.3 | Samsung Internet Full support 9.0 | Deno Full support 1.0 | Node.js Full support 10.0.0 |