try...catch

Конструкция try...catch пытается выполнить инструкции в блоке try, и, в случае ошибки, выполняет блок catch.

Синтаксис

try {
   try_statements
}
[catch (exception_var_1 if condition_1) { // не стандартно
   catch_statements_1
}]
...
[catch (exception_var_2) {
   catch_statements_2
}]
[finally {
   finally_statements
}]
try_statements

Инструкции для выполнения.

catch_statements_1, catch_statements_2

Инструкции, которые будут выполнены, если произойдёт ошибка в блоке try.

exception_var_1, exception_var_2

Идентификатор для хранения объекта ошибки, который впоследствии используется в блоке catch

condition_1

Условное выражение.

finally_statements

Инструкции, которые выполняются после завершения блока try. Выполнение происходит в независимости от того, произошла ошибка или нет.

Описание

Конструкция try содержит блок try, в котором находится одна или несколько инструкций (Блок ({} ) обязательно должен присутствовать, даже если выполняется всего одна инструкция), и хотя бы один блок catch или finally. Таким образом, есть три основные формы конструкции try:

  1. try {...} catch {...}
  2. try {...} finally {...}
  3. try {...} catch {...} finally {...}

Блок catch содержит инструкции, которые будут выполнены, если в блоке try произошла ошибка. Если любая инструкция в блоке try выбрасывает исключение, то управление сразу же переходит в блок catch. Если в блок try не было выброшено исключение, то блок catch не выполняется.

Блок finally выполнится после выполнения блоков try и catch, но перед инструкциями, следующими за конструкцией try...catch. Он выполняется всегда, в независимости от того, было исключение или нет.

Вы можете использовать вложенные конструкции try. Если внутренняя конструкция try не имеет блока catch (такое может быть при её использовании в виде try {...} finaly {...}, потому что try {...} не может быть без блоков catch или finally), будет вызван сatch внешней конструкции try.

Конструкция try также используется для обработки исключений JavaScript (то есть, выброшенных внутренними функциями языка или парсером). Загляните в JavaScript руководство для дополнительной информации о JavaScript исключениях.

Безусловный блок catch

При использовании блока catch, он вызывается для любого исключения в блоке try. Например, когда в следующем коде происходит ошибка, управление переходит к блоку catch.

js
try {
  throw "myException"; // создание исключения
} catch (e) {
  // инструкции для обработки ошибок
  logMyErrors(e); // передать объект исключения обработчику ошибок
}

Блок catch задаёт идентификатор (e в примере выше) который содержит объект исключения (в примере выше — значение, переданное оператору throw). Область видимости этого объекта ограничивается блоком catch.

Условный блок catch

"Условные блоки catch" можно создавать, используя try...catch с if...else if...else, как здесь:

js
try {
  myroutine(); // может выбрасывать три вида исключений
} catch (e) {
  if (e instanceof TypeError) {
    // обработка исключения TypeError
  } else if (e instanceof RangeError) {
    // обработка исключения RangeError
  } else if (e instanceof EvalError) {
    // обработка исключения EvalError
  } else {
    // обработка остальных исключений
    logMyErrors(e); // передать обработчику ошибок
  }
}

Частый сценарий использования — обработать известные исключения, а при неизвестных ошибках, пробросить их дальше:

js
try {
  myRoutine();
} catch (e) {
  if (e instanceof RangeError) {
    // обработка известного исключения, с которым
    // понятно, что делать
  } else {
    throw e; // пробросить неизвестные ошибки
  }
}

Примечание: Firefox раньше поддерживал краткую запись условных блоков catch:

try {
  myroutine(); // может выбрасывать три вида исключения
} catch (e if e instanceof TypeError) {
  // обработка исключений TypeError
} catch (e if e instanceof RangeError) {
  // обработка исключений RangeError
} catch (e if e instanceof EvalError) {
  // обработка исключений EvalError
} catch (e) {
  // обработка остальных исключения
  logMyErrors(e);
}

Однако, такой синтаксис никогда не был частью спецификации ECMAScript и был удалён из Firefox после версии 59. Сейчас он не поддерживается ни в одном браузере.

Идентификатор исключения

Когда в блоке try выбрасывается исключение, exception_var (т. е. e в конструкции catch (e)) содержит значение исключения. Его можно использовать, чтобы получить больше информации об выброшенном исключении. Идентификатор доступен только в области видимости блока catch.

js
try {
  if (!firstValidation()) {
    throw 1;
  }
  if (!secondValidation()) {
    throw 2;
  }
} catch (e) {
  // Выводит 1 или 2 (если не произошло никаких других ошибок)
  console.log(e);
}

Блок finally

Блок finally содержит код который будет запущен после кода в блоках try и catch. Обратите внимание, что код в блоке finally запускается в независимости от того, было ли выброшено исключение или нет. Также код в блоке finally будет запущен вне зависимости от того, присутствует блок catch или нет. Блок finally можно использовать для того, чтобы скрипт безопасно завершил работу в случае ошибки. Например, если необходимо освободить память и ресурсы которые использовал скрипт.

Наличие специального блока, связанного с ошибкой, который выполняется вне зависимости от наличия исключительной ситуации, может показаться странным, но эта конструкция на самом деле весьма полезна. Рассмотрим пример кода:

js
function expensiveCalculations() {
  // Сложные вычисления
}

function maybeThrowError() {
  // Функция, которая может выбросить исключение
  if (Math.random() > 0.5) throw new Error();
}

try {
  // Теперь при прокрутке страницы будут происходить
  // сложные вычисления, что сильно скажется на
  // производительности
  window.addEventListener("scroll", expensiveCalculations);
  maybeThrowError();
} catch {
  // Если функция maybeThrowError выбросит исключения,
  // управление сразу перейдёт в блок catch и
  // сложные вычисления продолжат выполняться до
  // перезагрузки страницы
  maybeThrowError();
}
window.removeEventListener("scroll", expensiveCalculations);

В этом примере, если функция maybeThrowError выбросит исключение внутри блока try, управление перейдёт в блок catch. Если и в блоке catch эта функция тоже выбросит исключение, то выполнение кода прервётся, и обработчик события не будет снят, пока пользователь не перезагрузит страницу, что плохо скажется на скорости работы. Для того, чтобы избежать таких ситуаций, следует использовать блок finally:

js
try {
  window.addEventListener("scroll", expensiveCalculations);
  maybeThrowError();
} catch {
  maybeThrowError();
} finally {
  window.removeEventListener("scroll", expensiveCalculations);
}

Другой пример: работа с файлами. В следующем фрагменте кода показывается, как скрипт открывает файл и записывает в него какие-то данные (в серверном окружении JavaScript имеет доступ к файловой системе). Во время записи может произойти ошибка. Но после открытия файл очень важно закрыть, потому что незакрытый файл может привести к утечкам памяти. В таких случаях используется блок finally:

js
openMyFile();
try {
  // Сделать что-то с файлом
  writeMyFile(theData);
} finally {
  closeMyFile(); // Закрыть файл, что бы ни произошло
}

Примеры

Вложенные блоки try

Для начала давайте посмотрим что делает этот код:

js
try {
  try {
    throw new Error("упс");
  } finally {
    console.log("finally");
  }
} catch (e) {
  console.error("внешний блок catch", e.message);
}

// Вывод:
// "finally"
// "внешний блок catch" "упс"

Теперь отловим исключение во внутреннем блоке try, добавив к нему блок catch:

js
try {
  try {
    throw new Error("упс");
  } catch (e) {
    console.error("внутренний блок catch", e.message);
  } finally {
    console.log("finally");
  }
} catch (e) {
  console.error("внешний блок catch", e.message);
}

// Output:
// "внутренний блок catch" "упс"
// "finally"

Наконец, пробросим ошибку

js
try {
  try {
    throw new Error("упс");
  } catch (e) {
    console.error("внутренний блок catch", e.message);
    throw e;
  } finally {
    console.log("finally");
  }
} catch (e) {
  console.error("внешний блок catch", e.message);
}

// Вывод:
// "внутренний блок catch" "oops"
// "finally"
// "внешний блок catch" "oops"

Любое исключение будет передано только в ближайший блок catch, если он не пробросит его дальше. Все исключения, выброшенными внутренними блоками (потому что код в блоке catch также может выбросить исключение), будут пойманы внешними.

Возвращение значения из блока finally

Если блок finally возвращает какое-либо значение, оно становится значением, которое возвращает вся конструкция try...catch...finally, вне зависимости от любых инструкций return в блоках try и catch. Также игнорируются исключения, выброшенные блоком catch.

js
(() => {
  try {
    try {
      throw new Error("oops");
    } catch (ex) {
      console.error("inner", ex.message);
      throw ex;
    } finally {
      console.log("finally");
      return;
    }
  } catch (ex) {
    console.error("outer", ex.message);
  }
})();

// Logs:
// "inner" "oops"
// "finally"

"упс" не доходит до внешнего блока из-за инструкции return в блоке finally. То же самое произойдёт с любым значением, возвращаемым из блока catch.

Спецификации

Specification
ECMAScript Language Specification
# sec-try-statement

Совместимость с браузерами

BCD tables only load in the browser

Смотрите также