For some reason this code gives me an uncaught exception error. It seems the catch block is not catching the error. Are try catch blocks scoped in such a way that I cannot throw an error in a nested function, and then expect it to be caught by a catch statement scoped higher up the chain? Some of the sensitive data with in the application that i’m working in has been removed, but it expected that leadInfo[ 0 / 1] would be a 32 character alpha numeric string that I pull from URL parameters.
The underlying issue here is with my AJAX call returning an error from the API and that error not being handled properly within the application. Hence the need for the throw statement. The AJAX call completes fine, and returns a JSON object that does not contain the email address as a property, so I need to handle that in a way that changes the page to reflect that.
jQuery(document).ready(function(){
try {
url = "http://api.com/api/v1/lead/" + leadInfo[1]
jQuery.ajax({
type: 'GET',
contentType: 'application/json',
url: url,
dataType : 'jsonp',
success: function (result) {
result = jQuery.parseJSON(result);
if(!result.data.email){
throw ('New exception');
}
console.log(result);
jQuery('.email').html(result.data.email);
}
});
jQuery('.surveryButton').click(function(){
window.location.replace("http://" + pgInventory.host + pgInventory.path + leadInfo[0] + "&curLeadId=" + leadInfo[1] + "&curViewedPages=0");
});
}
catch(err) {
jQuery('.email').html('your e-mail address');
jQuery('#arrowContent').remove();
}
});
flavian
27.9k11 gold badges63 silver badges103 bronze badges
asked May 1, 2013 at 10:55
The reason why your try catch block is failing is because an ajax request is asynchronous. The try catch block will execute before the Ajax call and send the request itself, but the error is thrown when the result is returned, AT A LATER POINT IN TIME.
When the try catch block is executed, there is no error. When the error is thrown, there is no try catch. If you need try catch for ajax requests, always put ajax try catch blocks inside the success callback, NEVER outside of it.
Here’s how you should do it:
success: function (result) {
try {
result = jQuery.parseJSON(result);
if (!result.data.email) {
throw ('New exception');
}
console.log(result);
jQuery('.email').html(result.data.email);
} catch (exception) {
console.error("bla");
};
}
answered May 1, 2013 at 10:58
flavianflavian
27.9k11 gold badges63 silver badges103 bronze badges
3
Due to the asynchronous nature of the callback methods in javascript, the context of the function throwing the error is different compared to the original one. You should do this way:
success: function (result) {
try {
result = jQuery.parseJSON(result);
if(!result.data.email){
throw ('New exception');
}
console.log(result);
jQuery('.email').html(result.data.email);
}
catch(err) {
// Dealing with the error
}
}
I would suggest you to have a look at this excellent article about the (very particular) contexts, closures and bindings in Javascript.
answered May 1, 2013 at 11:02
jap1968jap1968
7,6971 gold badge30 silver badges37 bronze badges
0
The problem is that ajax is asynchronous by definition. Your exception does not get thrown from within the $.ajax function, but from the callback function on success (which is triggered at a later time).
You should give an error: function(data) {} parameter to it as well, to handle server response errors, and furthermore you should place the try/catch block inside the callback function.
If you really want to catch it outside the callback, then you should consider calling a function rather than throwing an exception, because I don’t see how it can be done.
BenMorel
33.3k49 gold badges174 silver badges309 bronze badges
answered May 1, 2013 at 10:59
MarioDSMarioDS
12.8k15 gold badges63 silver badges121 bronze badges
I have a method in a class that is supposed to only throw error log messages and then another method in another function that will get this errors and console them (for now).
The problem is that it just throws the error in the console and not in an upper level…
This are the methods from file1.ts:
private sendToApp2(App2Url: string, logObject: object) {
try
{
let uriOptions = {
host: this.hostname,
port: this.port,
path: '/App2',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Api-version': '1'
}
};
this.App2Call(uriOptions, JSON.stringify(logObject));
}
catch (err) {
throw err;
}
}
private App2Call(params: Object, data: string) {
return new Promise((resolve, reject) => {
try {
let req = http.request(params, (response) => {
response.on('data', (data) => {
resolve(data);
});
}).on('error', (err: Error) => {
reject(new Error(err.message));
});
req.write(data);
req.end();
}
catch (err) {
reject(new Error(err.message));
}
});
}
And in the second file, I do this logic to catch the error and console it:
GetErrors() {
try {
// initialize the class object
let getErrors = new FirstClass();
await getErrors.sendToApp2(this.App2Url, this.logObject);
}
catch (err) {
console.log(err);
}
}
I have a method in a class that is supposed to only throw error log messages and then another method in another function that will get this errors and console them (for now).
The problem is that it just throws the error in the console and not in an upper level…
This are the methods from file1.ts:
private sendToApp2(App2Url: string, logObject: object) {
try
{
let uriOptions = {
host: this.hostname,
port: this.port,
path: '/App2',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Api-version': '1'
}
};
this.App2Call(uriOptions, JSON.stringify(logObject));
}
catch (err) {
throw err;
}
}
private App2Call(params: Object, data: string) {
return new Promise((resolve, reject) => {
try {
let req = http.request(params, (response) => {
response.on('data', (data) => {
resolve(data);
});
}).on('error', (err: Error) => {
reject(new Error(err.message));
});
req.write(data);
req.end();
}
catch (err) {
reject(new Error(err.message));
}
});
}
And in the second file, I do this logic to catch the error and console it:
GetErrors() {
try {
// initialize the class object
let getErrors = new FirstClass();
await getErrors.sendToApp2(this.App2Url, this.logObject);
}
catch (err) {
console.log(err);
}
}
Обработка ошибок, «try..catch»
Неважно, насколько мы хороши в программировании, иногда наши скрипты содержат ошибки. Они могут возникать из-за наших промахов, неожиданного ввода пользователя, неправильного ответа сервера и по тысяче других причин.
Обычно скрипт в случае ошибки «падает» (сразу же останавливается), с выводом ошибки в консоль.
Но есть синтаксическая конструкция try..catch, которая позволяет «ловить» ошибки и вместо падения делать что-то более осмысленное.
Синтаксис «try..catch»
Конструкция try..catch состоит из двух основных блоков: try, и затем catch:
try { // код... } catch (err) { // обработка ошибки }
Работает она так:
- Сначала выполняется код внутри блока
try {...}. - Если в нём нет ошибок, то блок
catch(err)игнорируется: выполнение доходит до концаtryи потом далее, полностью пропускаяcatch. - Если же в нём возникает ошибка, то выполнение
tryпрерывается, и поток управления переходит в началоcatch(err). Переменнаяerr(можно использовать любое имя) содержит объект ошибки с подробной информацией о произошедшем.
Таким образом, при ошибке в блоке try {…} скрипт не «падает», и мы получаем возможность обработать ошибку внутри catch.
Давайте рассмотрим примеры.
-
Пример без ошибок: выведет
alert(1)и(2):try { alert('Начало блока try'); // *!*(1) <--*/!* // ...код без ошибок alert('Конец блока try'); // *!*(2) <--*/!* } catch(err) { alert('Catch игнорируется, так как нет ошибок'); // (3) }
-
Пример с ошибками: выведет
(1)и(3):try { alert('Начало блока try'); // *!*(1) <--*/!* *!* lalala; // ошибка, переменная не определена! */!* alert('Конец блока try (никогда не выполнится)'); // (2) } catch(err) { alert(`Возникла ошибка!`); // *!*(3) <--*/!* }
««warn header=»try..catch работает только для ошибок, возникающих во время выполнения кода»
Чтобы `try..catch` работал, код должен быть выполнимым. Другими словами, это должен быть корректный JavaScript-код.
Он не сработает, если код синтаксически неверен, например, содержит несовпадающее количество фигурных скобок:
try { {{{{{{{{{{{{ } catch(e) { alert("Движок не может понять этот код, он некорректен"); }
JavaScript-движок сначала читает код, а затем исполняет его. Ошибки, которые возникают во время фазы чтения, называются ошибками парсинга. Их нельзя обработать (изнутри этого кода), потому что движок не понимает код.
Таким образом, try..catch может обрабатывать только ошибки, которые возникают в корректном коде. Такие ошибки называют «ошибками во время выполнения», а иногда «исключениями».
````warn header="`try..catch` работает синхронно"
Исключение, которое произойдёт в коде, запланированном "на будущее", например в `setTimeout`, `try..catch` не поймает:
```js run
try {
setTimeout(function() {
noSuchVariable; // скрипт упадёт тут
}, 1000);
} catch (e) {
alert( "не сработает" );
}
```
Это потому, что функция выполняется позже, когда движок уже покинул конструкцию `try..catch`.
Чтобы поймать исключение внутри запланированной функции, `try..catch` должен находиться внутри самой этой функции:
```js run
setTimeout(function() {
try {
noSuchVariable; // try..catch обрабатывает ошибку!
} catch {
alert( "ошибка поймана!" );
}
}, 1000);
```
Объект ошибки
Когда возникает ошибка, JavaScript генерирует объект, содержащий её детали. Затем этот объект передаётся как аргумент в блок catch:
try { // ... } catch(err) { // <-- объект ошибки, можно использовать другое название вместо err // ... }
Для всех встроенных ошибок этот объект имеет два основных свойства:
name
: Имя ошибки. Например, для неопределённой переменной это "ReferenceError".
message
: Текстовое сообщение о деталях ошибки.
В большинстве окружений доступны и другие, нестандартные свойства. Одно из самых широко используемых и поддерживаемых — это:
stack
: Текущий стек вызова: строка, содержащая информацию о последовательности вложенных вызовов, которые привели к ошибке. Используется в целях отладки.
Например:
try { *!* lalala; // ошибка, переменная не определена! */!* } catch(err) { alert(err.name); // ReferenceError alert(err.message); // lalala is not defined alert(err.stack); // ReferenceError: lalala is not defined at (...стек вызовов) // Можем также просто вывести ошибку целиком // Ошибка приводится к строке вида "name: message" alert(err); // ReferenceError: lalala is not defined }
Блок «catch» без переменной
[recent browser=new]
Если нам не нужны детали ошибки, в catch можно её пропустить:
try { // ... } catch { // <-- без (err) // ... }
Использование «try..catch»
Давайте рассмотрим реальные случаи использования try..catch.
Как мы уже знаем, JavaScript поддерживает метод JSON.parse(str) для чтения JSON.
Обычно он используется для декодирования данных, полученных по сети, от сервера или из другого источника.
Мы получаем их и вызываем JSON.parse вот так:
let json = '{"name":"John", "age": 30}'; // данные с сервера *!* let user = JSON.parse(json); // преобразовали текстовое представление в JS-объект */!* // теперь user - объект со свойствами из строки alert( user.name ); // John alert( user.age ); // 30
Вы можете найти более детальную информацию о JSON в главе info:json.
Если json некорректен, JSON.parse генерирует ошибку, то есть скрипт «падает».
Устроит ли нас такое поведение? Конечно нет!
Получается, что если вдруг что-то не так с данными, то посетитель никогда (если, конечно, не откроет консоль) об этом не узнает. А люди очень не любят, когда что-то «просто падает» без всякого сообщения об ошибке.
Давайте используем try..catch для обработки ошибки:
let json = "{ некорректный JSON }"; try { *!* let user = JSON.parse(json); // <-- тут возникает ошибка... */!* alert( user.name ); // не сработает } catch (e) { *!* // ...выполнение прыгает сюда alert( "Извините, в данных ошибка, мы попробуем получить их ещё раз." ); alert( e.name ); alert( e.message ); */!* }
Здесь мы используем блок catch только для вывода сообщения, но мы также можем сделать гораздо больше: отправить новый сетевой запрос, предложить посетителю альтернативный способ, отослать информацию об ошибке на сервер для логирования, … Всё лучше, чем просто «падение».
Генерация собственных ошибок
Что если json синтаксически корректен, но не содержит необходимого свойства name?
Например, так:
let json = '{ "age": 30 }'; // данные неполны try { let user = JSON.parse(json); // <-- выполнится без ошибок *!* alert( user.name ); // нет свойства name! */!* } catch (e) { alert( "не выполнится" ); }
Здесь JSON.parse выполнится без ошибок, но на самом деле отсутствие свойства name для нас ошибка.
Для того, чтобы унифицировать обработку ошибок, мы воспользуемся оператором throw.
Оператор «throw»
Оператор throw генерирует ошибку.
Синтаксис:
Технически в качестве объекта ошибки можно передать что угодно. Это может быть даже примитив, число или строка, но всё же лучше, чтобы это был объект, желательно со свойствами name и message (для совместимости со встроенными ошибками).
В JavaScript есть множество встроенных конструкторов для стандартных ошибок: Error, SyntaxError, ReferenceError, TypeError и другие. Можно использовать и их для создания объектов ошибки.
Их синтаксис:
let error = new Error(message); // или let error = new SyntaxError(message); let error = new ReferenceError(message); // ...
Для встроенных ошибок (не для любых объектов, только для ошибок), свойство name — это в точности имя конструктора. А свойство message берётся из аргумента.
Например:
let error = new Error(" Ого, ошибка! o_O"); alert(error.name); // Error alert(error.message); // Ого, ошибка! o_O
Давайте посмотрим, какую ошибку генерирует JSON.parse:
try { JSON.parse("{ bad json o_O }"); } catch(e) { *!* alert(e.name); // SyntaxError */!* alert(e.message); // Unexpected token b in JSON at position 2 }
Как мы видим, это SyntaxError.
В нашем случае отсутствие свойства name — это ошибка, ведь пользователи должны иметь имена.
Сгенерируем её:
let json = '{ "age": 30 }'; // данные неполны try { let user = JSON.parse(json); // <-- выполнится без ошибок if (!user.name) { *!* throw new SyntaxError("Данные неполны: нет имени"); // (*) */!* } alert( user.name ); } catch(e) { alert( "JSON Error: " + e.message ); // JSON Error: Данные неполны: нет имени }
В строке (*) оператор throw генерирует ошибку SyntaxError с сообщением message. Точно такого же вида, как генерирует сам JavaScript. Выполнение блока try немедленно останавливается, и поток управления прыгает в catch.
Теперь блок catch становится единственным местом для обработки всех ошибок: и для JSON.parse и для других случаев.
Проброс исключения
В примере выше мы использовали try..catch для обработки некорректных данных. А что, если в блоке try {...} возникнет другая неожиданная ошибка? Например, программная (неопределённая переменная) или какая-то ещё, а не ошибка, связанная с некорректными данными.
Пример:
let json = '{ "age": 30 }'; // данные неполны try { user = JSON.parse(json); // <-- забыл добавить "let" перед user // ... } catch(err) { alert("JSON Error: " + err); // JSON Error: ReferenceError: user is not defined // (не JSON ошибка на самом деле) }
Конечно, возможно все! Программисты совершают ошибки. Даже в утилитах с открытым исходным кодом, используемых миллионами людей на протяжении десятилетий — вдруг может быть обнаружена ошибка, которая приводит к ужасным взломам.
В нашем случае try..catch предназначен для выявления ошибок, связанных с некорректными данными. Но по своей природе catch получает все свои ошибки из try. Здесь он получает неожиданную ошибку, но всё также показывает то же самое сообщение "JSON Error". Это неправильно и затрудняет отладку кода.
К счастью, мы можем выяснить, какую ошибку мы получили, например, по её свойству name:
try { user = { /*...*/ }; } catch(e) { *!* alert(e.name); // "ReferenceError" из-за неопределённой переменной */!* }
Есть простое правило:
Блок catch должен обрабатывать только те ошибки, которые ему известны, и «пробрасывать» все остальные.
Техника «проброс исключения» выглядит так:
- Блок
catchполучает все ошибки. - В блоке
catch(err) {...}мы анализируем объект ошибкиerr. - Если мы не знаем как её обработать, тогда делаем
throw err.
В коде ниже мы используем проброс исключения, catch обрабатывает только SyntaxError:
let json = '{ "age": 30 }'; // данные неполны try { let user = JSON.parse(json); if (!user.name) { throw new SyntaxError("Данные неполны: нет имени"); } *!* blabla(); // неожиданная ошибка */!* alert( user.name ); } catch(e) { *!* if (e.name == "SyntaxError") { alert( "JSON Error: " + e.message ); } else { throw e; // проброс (*) } */!* }
Ошибка в строке (*) из блока catch «выпадает наружу» и может быть поймана другой внешней конструкцией try..catch (если есть), или «убьёт» скрипт.
Таким образом, блок catch фактически обрабатывает только те ошибки, с которыми он знает, как справляться, и пропускает остальные.
Пример ниже демонстрирует, как такие ошибки могут быть пойманы с помощью ещё одного уровня try..catch:
function readData() { let json = '{ "age": 30 }'; try { // ... *!* blabla(); // ошибка! */!* } catch (e) { // ... if (e.name != 'SyntaxError') { *!* throw e; // проброс исключения (не знаю как это обработать) */!* } } } try { readData(); } catch (e) { *!* alert( "Внешний catch поймал: " + e ); // поймал! */!* }
Здесь readData знает только, как обработать SyntaxError, тогда как внешний блок try..catch знает, как обработать всё.
try..catch..finally
Подождите, это ещё не всё.
Конструкция try..catch может содержать ещё одну секцию: finally.
Если секция есть, то она выполняется в любом случае:
- после
try, если не было ошибок, - после
catch, если ошибки были.
Расширенный синтаксис выглядит следующим образом:
*!*try*/!* { ... пробуем выполнить код... } *!*catch*/!*(e) { ... обрабатываем ошибки ... } *!*finally*/!* { ... выполняем всегда ... }
Попробуйте запустить такой код:
try { alert( 'try' ); if (confirm('Сгенерировать ошибку?')) BAD_CODE(); } catch (e) { alert( 'catch' ); } finally { alert( 'finally' ); }
У кода есть два пути выполнения:
- Если вы ответите на вопрос «Сгенерировать ошибку?» утвердительно, то
try -> catch -> finally. - Если ответите отрицательно, то
try -> finally.
Секцию finally часто используют, когда мы начали что-то делать и хотим завершить это вне зависимости от того, будет ошибка или нет.
Например, мы хотим измерить время, которое занимает функция чисел Фибоначчи fib(n). Естественно, мы можем начать измерения до того, как функция начнёт выполняться и закончить после. Но что делать, если при вызове функции возникла ошибка? В частности, реализация fib(n) в коде ниже возвращает ошибку для отрицательных и для нецелых чисел.
Секция finally отлично подходит для завершения измерений несмотря ни на что.
Здесь finally гарантирует, что время будет измерено корректно в обеих ситуациях — и в случае успешного завершения fib и в случае ошибки:
let num = +prompt("Введите положительное целое число?", 35) let diff, result; function fib(n) { if (n < 0 || Math.trunc(n) != n) { throw new Error("Должно быть целое неотрицательное число"); } return n <= 1 ? n : fib(n - 1) + fib(n - 2); } let start = Date.now(); try { result = fib(num); } catch (e) { result = 0; *!* } finally { diff = Date.now() - start; } */!* alert(result || "возникла ошибка"); alert( `Выполнение заняло ${diff}ms` );
Вы можете это проверить, запустив этот код и введя 35 в prompt — код завершится нормально, finally выполнится после try. А затем введите -1 — незамедлительно произойдёт ошибка, выполнение займёт 0ms. Оба измерения выполняются корректно.
Другими словами, неважно как завершилась функция: через return или throw. Секция finally срабатывает в обоих случаях.
«`smart header=»Переменные внутри try..catch..finally локальны»
Обратите внимание, что переменные `result` и `diff` в коде выше объявлены до `try..catch`.
Если переменную объявить в блоке, например, в try, то она не будет доступна после него.
````smart header="`finally` и `return`"
Блок `finally` срабатывает при *любом* выходе из `try..catch`, в том числе и `return`.
В примере ниже из `try` происходит `return`, но `finally` получает управление до того, как контроль возвращается во внешний код.
```js run
function func() {
try {
*!*
return 1;
*/!*
} catch (e) {
/* ... */
} finally {
*!*
alert( 'finally' );
*/!*
}
}
alert( func() ); // сначала срабатывает alert из finally, а затем этот код
````smart header="`try..finally`"
Конструкция `try..finally` без секции `catch` также полезна. Мы применяем её, когда не хотим здесь обрабатывать ошибки (пусть выпадут), но хотим быть уверены, что начатые процессы завершились.
```js
function func() {
// начать делать что-то, что требует завершения (например, измерения)
try {
// ...
} finally {
// завершить это, даже если все упадёт
}
}
```
В приведённом выше коде ошибка всегда выпадает наружу, потому что тут нет блока `catch`. Но `finally` отрабатывает до того, как поток управления выйдет из функции.
Глобальный catch
Информация из данной секции не является частью языка JavaScript.
Давайте представим, что произошла фатальная ошибка (программная или что-то ещё ужасное) снаружи try..catch, и скрипт упал.
Существует ли способ отреагировать на такие ситуации? Мы можем захотеть залогировать ошибку, показать что-то пользователю (обычно они не видят сообщение об ошибке) и т.д.
Такого способа нет в спецификации, но обычно окружения предоставляют его, потому что это весьма полезно. Например, в Node.js для этого есть process.on("uncaughtException"). А в браузере мы можем присвоить функцию специальному свойству window.onerror, которая будет вызвана в случае необработанной ошибки.
Синтаксис:
window.onerror = function(message, url, line, col, error) { // ... };
message
: Сообщение об ошибке.
url
: URL скрипта, в котором произошла ошибка.
line, col
: Номера строки и столбца, в которых произошла ошибка.
error
: Объект ошибки.
Пример:
<script> *!* window.onerror = function(message, url, line, col, error) { alert(`${message}n В ${line}:${col} на ${url}`); }; */!* function readData() { badFunc(); // Ой, что-то пошло не так! } readData(); </script>
Роль глобального обработчика window.onerror обычно заключается не в восстановлении выполнения скрипта — это скорее всего невозможно в случае программной ошибки, а в отправке сообщения об ошибке разработчикам.
Существуют также веб-сервисы, которые предоставляют логирование ошибок для таких случаев, такие как https://errorception.com или http://www.muscula.com.
Они работают так:
- Мы регистрируемся в сервисе и получаем небольшой JS-скрипт (или URL скрипта) от них для вставки на страницы.
- Этот JS-скрипт ставит свою функцию
window.onerror. - Когда возникает ошибка, она выполняется и отправляет сетевой запрос с информацией о ней в сервис.
- Мы можем войти в веб-интерфейс сервиса и увидеть ошибки.
Итого
Конструкция try..catch позволяет обрабатывать ошибки во время исполнения кода. Она позволяет запустить код и перехватить ошибки, которые могут в нём возникнуть.
Синтаксис:
try { // исполняем код } catch(err) { // если случилась ошибка, прыгаем сюда // err - это объект ошибки } finally { // выполняется всегда после try/catch }
Секций catch или finally может не быть, то есть более короткие конструкции try..catch и try..finally также корректны.
Объекты ошибок содержат следующие свойства:
message— понятное человеку сообщение.name— строка с именем ошибки (имя конструктора ошибки).stack(нестандартное, но хорошо поддерживается) — стек на момент ошибки.
Если объект ошибки не нужен, мы можем пропустить его, используя catch { вместо catch(err) {.
Мы можем также генерировать собственные ошибки, используя оператор throw. Аргументом throw может быть что угодно, но обычно это объект ошибки, наследуемый от встроенного класса Error. Подробнее о расширении ошибок см. в следующей главе.
Проброс исключения — это очень важный приём обработки ошибок: блок catch обычно ожидает и знает, как обработать определённый тип ошибок, поэтому он должен пробрасывать дальше ошибки, о которых он не знает.
Даже если у нас нет try..catch, большинство сред позволяют настроить «глобальный» обработчик ошибок, чтобы ловить ошибки, которые «выпадают наружу». В браузере это window.onerror.
Конструкция 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:
try {...} catch {...}try {...} finally {...}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.
try {
throw 'myException'; // создание исключения
}
catch (e) {
// инструкции для обработки ошибок
logMyErrors(e); // передать объект исключения обработчику ошибок
}
Блок catch задаёт идентификатор (e в примере выше) который содержит объект исключения (в примере выше — значение, переданное оператору throw). Область видимости этого объекта ограничивается блоком 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; // пробросить неизвестные ошибки
}
}
Примечание: Обратите внимание: 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.
try {
if (!firstValidation()) {
throw 1;
}
if (!secondValidation()) {
throw 2;
}
} catch (e) {
// Выводит 1 или 2 (если не произошло никаких других ошибок)
console.log(e);
}
Блок finally
Блок finally содержит код который будет запущен после кода в блоках try и catch. Обратите внимание, что код в блоке finally запускается в независимости от того, было ли выброшено исключение или нет. Также код в блоке finally будет запущен вне зависимости от того, присутствует блок catch или нет. Блок finally можно использовать для того, чтобы скрипт безопасно завершил работу в случае ошибки. Например, если необходимо освободить память и ресурсы которые использовал скрипт.
Наличие специального блока, связанного с ошибкой, который выполняется вне зависимости от наличия исключительной ситуации, может показаться странным, но эта конструкция на самом деле весьма полезна. Рассмотрим пример кода:
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:
try {
window.addEventListener('scroll', expensiveCalculations)
maybeThrowError()
} catch {
maybeThrowError()
} finally {
window.removeEventListener('scroll', expensiveCalculations)
}
Другой пример: работа с файлами. В следующем фрагменте кода показывается, как скрипт открывает файл и записывает в него какие-то данные (в серверном окружении JavaScript имеет доступ к файловой системе). Во время записи может произойти ошибка. Но после открытия файл очень важно закрыть, потому что незакрытый файл может привести к утечкам памяти. В таких случаях используется блок finally:
openMyFile();
try {
// Сделать что-то с файлом
writeMyFile(theData);
}
finally {
closeMyFile(); // Закрыть файл, что бы ни произошло
}
Примеры
Вложенные блоки try
Для начала давайте посмотрим что делает этот код:
try {
try {
throw new Error('упс');
}
finally {
console.log('finally');
}
}
catch (e) {
console.error('внешний блок catch', e.message);
}
// Вывод:
// "finally"
// "внешний блок catch" "упс"
Теперь отловим исключение во внутреннем блоке try, добавив к нему блок catch:
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"
Наконец, пробросим ошибку
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.
try {
try {
throw new Error('упс');
}
catch (e) {
console.error('внутренний блок catch', e.message);
throw e;
}
finally {
console.log('finally');
return;
}
}
catch (e) {
console.error('внешний блок catch', e.message);
}
// Output:
// "внутренний блок catch" "упс"
// "finally"
«упс» не доходит до внешнего блока из-за инструкции return в блоке finally. То же самое произойдёт с любым значением, возвращаемым из блока catch.
Спецификации
| Specification |
|---|
| ECMAScript Language Specification # sec-try-statement |
Совместимость
BCD tables only load in the browser