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
Chrome Edge Firefox Internet Explorer Opera Safari WebView Android Chrome Android Firefox for Android Opera Android Safari on iOS Samsung Internet Deno Node.js
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 No
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

Дивіться також