С помощью JavaScript выражения try..catch Вы можете проверять участки кода на наличие ошибок.
Выражение try..catch
Выражение try..catch позволяет проверять участки кода на наличие ошибок.
Блок try содержит код, который проверяется на ошибки.
Блок catch содержит код, который будет выполнен если в блоке try будет найдена ошибка.
Синтаксис:
try {
//Код, который проверяется на наличие ошибок
}
catch(ошибка) {
//Код, который будет выполнен если в блоке try были найдены ошибки
}
ошибка содержит пояснение к ошибке найденной в блоке try (если ошибка не была найдена переменная останется пустой).
Пример
//Проверим участок кода на ошибки
try
{
//В данном коде ошибка (dddocument вместо document)
dddocument.write('Привет всем!');
}
catch (er)
{
//Выведем пояснение к ошибке
document.write(er);
}
Быстрый просмотр
Блок finally
Данный блок является необязательной частью конструкции try..catch.
Код находящийся в данном блоке начинает выполняться после исполнения кода в блоках try и catch, но перед исполнением команд, которые следуют за данной конструкцией.
Обратите внимание: код в данном блоке будет выполнен независимо от того были ли найдена ошибка в блоке try или нет.
Пример
try
{
document.write('Привет всем!');
}
finally
{
document.write('<hr />Проверка ошибок была завершена успешно');
}
Быстрый просмотр
Команда throw
Если автоматически сгенерированное пояснение к ошибке Вас не устраивает можете использовать команду throw, чтобы создавать собственные пояснения к возможным ошибкам.
Синтаксис:
Пример
f=prompt('Введите число от 1 до 5: ','');
try {
if (f>5) {
throw 'Число которые Вы ввели больше 5';
}
if (f<1) {
throw 'Число которое Вы ввели меньше 1';
}
if (isNaN(f)) {
throw 'То, что Вы ввели не является числом';
}
else {
document.write('Вы ввели: ' + f);
}
}
catch (er) {
document.write(er);
}
Сделайте сами
Задание 1. Найдите с помощью try..catch ошибки в коде ниже и исправьте их.
Задание 1
docuemnt.write('Первое сообщение');
document.wirte('Второе сообщение');
getElementById('mes').innerHTML="Третье сообщение";
getElemeNtById('mes1').innerHTML="Четвертое сообщение";
Оператор try позволяет вам проверить блок кода на наличие ошибок.
Оператор catch позволяет вам обработать ошибку.
Оператор throw позволяет создавать собственные ошибки.
Оператор finally позволяет выполнить код, после того, как попытаться поймать, независимо от результата.
Ошибки будут!
При выполнении кода JavaScript могут возникать разные ошибки.
Ошибки могут быть ошибками кодирования, допущенны программистом, ошибками из-за неправильного ввода или другими непредвиденными ситуациями.
Пример
В этом примере мы в место alert написали adddlert, чтобы намеренно выдать ошибку:
<p id=»demo»></p>
<script>
try {
adddlert(«Добро пожаловать!»);
}
catch(err) {
document.getElementById(«demo»).innerHTML = err.message;
}
</script>
Попробуйте сами »
JavaScript перехватывает adddlert, как ошибку и выполняет код перехвата для ее обработки.
JavaScript try и catch
Оператор try, позволяет обнаружить блок кода, который будет проверяться на наличие ошибок во время его выполнения.
Оператор catch, позволяет поймать блок кода, который будет выполняться, если в блоке try возникает ошибка.
Оператор JavaScript try и catch идут в парах:
try {
Блок кода для проверки
}
catch(err) {
Блок кода для обработки ошибок
}
JavaScript выдает ошибки
Когда происходит ошибка, JavaScript обычно останавливается и генерирует сообщение об ошибке.
Технический термин для этого: JavaScript вызовет исключение (выдаст ошибку).
JavaScript фактически создаст объект Error с двумя свойствами: name и message.
Оператор throw
Оператор throw позволяет создать пользовательскую ошибку.
Технически вы можете сгенерировать исключение (сгенерировать ошибку).
Исключением может быть JavaScript String, Number, Boolean или Object:
throw «Слишком большой»; // пропустить текст
throw 500; // пропустить число
Если вы используете throw вместе с try и catch, вы можете контролировать выполнение программы и генерировать собственные сообщения об ошибках.
Пример проверки ввода
В этом примере исследуется ввод. Если значение неверно, генерируется исключение (ошибка).
Исключение (err) перехватывается оператором catch, и отображается настраиваемое сообщение об ошибке:
<!DOCTYPE html>
<html>
<body>
<p>Пожалуйста, введите число от 5 до 10:</p>
<input id=»demo» type=»text»>
<button type=»button»
onclick=»myFunction()»>Ввод текста</button>
<p id=»p01″></p>
<script>
function myFunction() {
var message, x;
message =
document.getElementById(«p01»);
message.innerHTML = «»;
x =
document.getElementById(«demo»).value;
try {
if(x == «») throw «пусто»;
if(isNaN(x)) throw «не число»;
x = Number(x);
if(x < 5) throw
«слишком маленькое»;
if(x > 10) throw «слишком
большое»;
}
catch(err) {
message.innerHTML =
«Вывод » + err;
}
}
</script>
</body>
</html>
Попробуйте сами »
Проверка HTML на валидность
Код выше — является просто примером.
Современные браузеры часто используют комбинацию JavaScript и встроенной проверки HTML, используя предопределенные правила проверки, определенные в атрибутах HTML:
<input id=»demo» type=»number» min=»5″ max=»10″ step=»1″>
Вы можете узнать больше о проверке форм в следующей главе этого руководства.
Оператор finally
Оператор finally позволяет выполнить код, после try и catch, еще раз попытаться поймать, независимо от результата:
Синтаксис
try {
блок кода для попытки обнаружить ошибки
}
catch(err) {
блок кода для обработки поймать ошибки
}
finally {
блок кода, который будет выполняться независимо от результата try/catch
}
Пример
function myFunction() {
var message, x;
message =
document.getElementById(«p01»);
message.innerHTML = «»;
x =
document.getElementById(«demo»).value;
try {
if(x == «») throw «пусто»;
if(isNaN(x))
throw «не число»;
x = Number(x);
if(x >
10) throw «слишком большое»;
if(x <
5) throw «слишком маленькое»;
}
catch(err)
{
message.innerHTML = «Ошибка: » +
err + «.»;
}
finally {
document.getElementById(«demo»).value = «»;
}
}
Попробуйте сами »
Объект Error
JavaScript имеет встроенный объект Error, который предоставляет информацию об ошибке при возникновении ошибки.
Объект Error предоставляет два полезных свойства: имя и сообщение.
Свойства объекта Error
| Свойство | Описание |
|---|---|
| имя | Задает или возвращает имя ошибки |
| сообщение | Устанавливает или возвращает сообщение об ошибке (строку) |
Значения Error Name
Шесть различных значений могут быть возвращены свойством Error Name:
| Error Name | Описание |
|---|---|
| EvalError | Произошла ошибка в функции eval() |
| RangeError | Произошло число «вне допустимого диапазона» |
| ReferenceError | Произошла недопустимая ссылка |
| SyntaxError | Произошла синтаксическая ошибка |
| TypeError | Произошла ошибка типа |
| URIError | Произошла ошибка в encodeURI() |
Ниже описаны шесть различных значений.
Ошибка EvalError
Один EvalError указывает на ошибку в функции eval().
Новые версии JavaScript не генерируют EvalError. Вместо этого используйте SyntaxError.
Ошибка RangeError
Ошибка RangeError генерируется, если вы используете число, выходящее за пределы диапазона допустимых значений.
Например: Вы не можете установить количество значащих цифр числа на 500.
Пример
var num = 1;
try {
num.toPrecision(500); // Число не может содержать 500 значащих цифр
}
catch(err) {
document.getElementById(«demo»).innerHTML = err.name;
}
Попробуйте сами »
Ошибка ReferenceError
Ошибка ReferenceError генерируется, если вы используете (ссылаетесь на неё) переменную,
которая не была объявлена:
Пример
var x;
try {
x = y + 1; // на y нельзя ссылаться (использовать)
}
catch(err) {
document.getElementById(«demo»).innerHTML = err.name;
}
Попробуйте сами »
Ошибка SyntaxError
Ошибка SyntaxError генерируется, если вы пытаетесь запустить код с синтаксической ошибкой.
Пример
try {
eval(«alert(‘Привет)»); // Отсутствует ‘вызовет ошибку
}
catch(err) {
document.getElementById(«demo»).innerHTML = err.name;
}
Попробуйте сами »
Ошибка TypeError
Ошибка TypeError генерируется, если вы используете значение, которое находится за пределами диапазона ожидаемых типов:
Пример
var num = 1;
try {
num.toUpperCase(); // Вы не можете преобразовать число в верхний регистр
}
catch(err) {
document.getElementById(«demo»).innerHTML = err.name;
}
Попробуйте сами »
Ошибка URIError (Единый идентификатор ресурса)
Ошибка URIError генерируется, если вы используете недопустимые символы в функции URI:
Пример
try {
decodeURI(«%%%»); // Вы не можете декодировать знаки процента URI
}
catch(err) {
document.getElementById(«demo»).innerHTML = err.name;
}
Попробуйте сами »
Нестандартные свойства объекта ошибок
Mozilla и Microsoft определяют некоторые нестандартные свойства объекта ошибки:
fileName (Mozilla)
lineNumber (Mozilla)
columnNumber (Mozilla)
stack (Mozilla)
description (Microsoft)
number (Microsoft)
Не используйте эти свойства на общедоступных веб-сайтах. Они не будут работать во всех браузерах.
Полный справочник ошибок
Чтобы получить полную информацию об объекте Error, перейдите в Полный справочник ошибок JavaScript ..
Оператор try позволяет проверить блок кода на возникновение ошибок.
Оператор catch позволяет обрабатывать возникшую ошибку.
Оператор throw позволяет генерировать пользовательские ошибки.
Оператор finally позволяет выполнять код после операторов try и catch не зависимо от результата.
Ошибки будут!
Во время выполнения кода JavaScript, могут возникать разные ошибки.
Это могут быть ошибки кодирования, сделанные программистом, ошибки, возникающие в результате ввода неверных данных, и другие непредвиденные вещи.
В следующем примере, чтобы умышленно создать ошибку, мы вместо команды alert написали adddlert:
<p id="demo"></p>
<script>
try {
adddlert("Добро пожаловать, гость!");
}
catch(err) {
document.getElementById("demo").innerHTML = err.message;
}
</script>
JavaScript перехватит adddlert как ошибку и выполнит код в операторе catch, чтобы ее обработать.
Операторы try и catch
Оператор try определяет блок кода, который во время выполнения будет проверяться на возникновение ошибок.
Оператор catch определяет блок кода, который будет выполняться, если в блоке оператора try возникнет ошибка.
Операторы try и catch всегда используются в паре:
try {
// проверяемый блок кода
}
catch(err) {
// блок кода для обработки ошибок
}
JavaScript генерирует ошибки
Когда возникает ошибка, обычно интерпретатор JavaScript останавливает выполнение кода и генерирует сообщение об ошибке.
Если говорить техническими терминами, то интерпретатор JavaScript сгенерирует исключение (сгенерирует ошибку).
На самом деле интерпретатор JavaScript создаст объект Error с двумя свойствами — name и message.
Оператор throw
Оператор throw позволяет создавать пользовательские ошибки.
В техническом смысле вы можете генерировать исключения (генерировать ошибки).
Исключения могут быть строкой, числом, логическим значением или объектом JavaScript:
throw "Слишком большой";
throw 500;
Используя оператор throw вместе с try и catch, вы можете контролировать ход программы и генерировать пользовательские сообщения об ошибках.
Пример проверки ввода
В следующем примере проверяются данные ввода. Если введено неверное значение, то генерируется исключение (err).
Исключение (err) перехватывается оператором catch, и выводится пользовательское сообщение об ошибке:
<!DOCTYPE html>
<html>
<body>
<p>Введите число от 5 до 10:</p>
<input id="demo" type="text">
<button type="button" onclick="myFunction()">Проверка ввода</button>
<p id="p01"></p>
<script>
function myFunction() {
var message, x;
message = document.getElementById("p01");
message.innerHTML = "";
x = document.getElementById("demo").value;
try {
if(x == "") throw "пусто";
if(isNaN(x)) throw "не число";
x = Number(x);
if(x < 5) throw "слишком мало";
if(x > 10) throw "слишком много";
}
catch(err) {
message.innerHTML = "Вы ввели " + err;
}
}
</script>
</body>
</html>
HTML проверка
Приведенный выше код это всего лишь пример.
Современные браузеры часто используют комбинацию JavaScript и встроенной HTML проверки, реализуемой при помощи предопределенных правил, заданных в HTML атрибутах:
<input id="demo" type="number" min="5" max="10" step="1" >
Оператор finally
Оператор finally позволяет выполнить код после операторов try и catch, независимо от результатов проверки:
try {
// проверяемый блок кода
}
catch(err) {
// блок кода для обработки ошибок
}
finally {
// блок кода, выполняемый не зависимо от результата операторов try / catch
}
Пример:
function myFunction() {
var message, x;
message = document.getElementById("p01");
message.innerHTML = "";
x = document.getElementById("demo").value;
try {
if(x == "") throw "пусто";
if(isNaN(x)) throw "не число";
x = Number(x);
if(x < 5) throw "слишком мало";
if(x > 10) throw "слишком много";
}
catch(err) {
message.innerHTML = "Ошибка: " + err + ".";
}
finally {
document.getElementById("demo").value = "";
}
}
Объект Error
В JavaScript есть встроенный объект Error, который предоставляет информацию по возникшей ошибке.
Объект Error предоставляет два полезных свойства:
| Свойство | Описание |
|---|---|
| name | Устанавливает или возвращает имя ошибки |
| message | Устанавливает или возвращает сообщение об ошибке (строку) |
Значения свойства name
В свойстве name объекта Error может быть возвращено шесть различных значений:
| Имя ошибки | Описание |
|---|---|
| EvalError | Ошибка в функции eval() |
| RangeError | Число «за пределами диапазона» |
| ReferenceError | Недопустимая ссылка (обращение) |
| SyntaxError | Синтаксическая ошибка |
| TypeError | Ошибка типов |
| URIError | Ошибка в encodeURI() |
EvalError
EvalError указывает на то, что возникла ошибка в функции eval().
В более новых версиях JavaScript никогда не генерируется ошибка EvalError. Вместо нее используется SyntaxError.
RangeError
Ошибка RangeError возникает, если вы используете число, которое выходит за пределы диапазона допустимых значений.
Например, нельзя задать число с 500 знаковыми цифрами.
var num = 1;
try {
num.toPrecision(500); // Число не может иметь 500 знаковых цифр
}
catch(err) {
document.getElementById("demo").innerHTML = err.name;
}
ReferenceError
Ошибка ReferenceError возникает в том случае, когда вы используете (ссылаетесь) переменную, которая не была декларирована:
var x;
try {
x = y + 1; // нельзя использовать (ссылаться на) переменную y
}
catch(err) {
document.getElementById("demo").innerHTML = err.name;
}
SyntaxError
Ошибка SyntaxError возникает, когда вы пытаетесь выполнить код с синтаксическими ошибками.
try {
eval("alert('Hello)"); // Пропуск ' приведет к синтаксической ошибке
}
catch(err) {
document.getElementById("demo").innerHTML = err.name;
}
TypeError
Ошибка TypeError возникает, когда вы используете значение, выходящее за пределы диапазона ожидаемых типов:
var num = 1;
try {
num.toUpperCase(); // Нельзя преобразовать число в верхний регистр
}
catch(err) {
document.getElementById("demo").innerHTML = err.name;
}
URIError
Ошибка URIError возникает, когда вы используете недопустимые символы в функциях URI:
try {
decodeURI("%%%"); // Нельзя декодировать эти символы процентов
}
catch(err) {
document.getElementById("demo").innerHTML = err.name;
}
Нестандартные свойства объекта Error
Mozilla и Microsoft определяют ряд нестандартных свойств объекта Error:
fileName (Mozilla)
lineNumber (Mozilla)
columnNumber (Mozilla)
stack (Mozilla)
description (Microsoft)
number (Microsoft)
Никогда не используйте эти свойства в публичном веб-сайте. Они не будут работать во всех браузерах.
Обработка ошибок, «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.
Порядок выполнения и обработка ошибок
- « Предыдущая статья
- Следующая статья »
JavaScript поддерживает компактный набор инструкций, особенно управляющих инструкций, которые вы можете использовать, чтобы реализовать интерактивность в вашем приложении. В данной главе даётся обзор этих инструкций.
Более подробная информация об инструкциях, рассмотренных в данной главе, содержится в справочнике по JavaScript. Точка с запятой (;) используется для разделения инструкций в коде.
Любое выражение (expression) в JavaScript является также инструкцией (statement). Чтобы получить более подробную информацию о выражениях, прочитайте Выражения и операторы.
Инструкция block
Инструкция block является фундаментальной и используется для группировки других инструкций. Блок ограничивается фигурными скобками:
{ statement_1; statement_2; ... statement_n; }
Блок обычно используется с управляющими инструкциями (например, if, for, while).
В вышеприведённом примере { x++; } является блоком.
Обратите внимание: в JavaScript отсутствует область видимости блока до ECMAScript2015. Переменные, объявленные внутри блока, имеют область видимости функции (или скрипта), в которой находится данный блок, вследствие чего они сохранят свои значения при выходе за пределы блока. Другими словами, блок не создаёт новую область видимости. «Автономные» (standalone) блоки в JavaScript могут продуцировать полностью отличающийся результат, от результата в языках C или Java. Например:
var x = 1;
{
var x = 2;
}
console.log(x); // выведет 2
В вышеприведённом примере инструкция var x внутри блока находится в той же области видимости, что и инструкция var x перед блоком. В C или Java эквивалентный код выведет значение 1.
Начиная с ECMAScript 6, оператор let позволяет объявить переменную в области видимости блока. Чтобы получить более подробную информацию, прочитайте let.
Условные инструкции
Условная инструкция — это набор команд, которые выполняются, если указанное условие является истинным. JavaScript поддерживает две условные инструкции: if...else и switch.
Инструкция if…else
Используйте оператор if для выполнения инструкции, если логическое условия истинно. Используйте опциональный else, для выполнения инструкции, если условие ложно. Оператор if выглядит так:
if (condition) {
statement_1;
} else {
statement_2;
}
Здесь condition может быть любым выражением, вычисляемым как истинное (true) или ложное (false). Чтобы получить более подробную информацию о значениях true и false, прочитайте Boolean. Если условие оценивается как true, то выполняется statement_1, в противном случае — statement_2. Блоки statement_1 и statement_2 могут быть любыми блоками, включая также вложенные инструкции if.
Также вы можете объединить несколько инструкций, пользуясь else if для получения последовательности проверок условий:
if (condition_1) { statement_1;} else if (condition_2) { statement_2;} else if (condition_n) { statement_n; } else { statement_last;}
В случае нескольких условий только первое логическое условие, которое вычислится истинным (true), будет выполнено. Используйте блок ({ ... }) для группировки нескольких инструкций. Применение блоков является хорошей практикой, особенно когда используются вложенные инструкции if:
if (condition) {
statement_1_runs_if_condition_is_true;
statement_2_runs_if_condition_is_true;
} else {
statement_3_runs_if_condition_is_false;
statement_4_runs_if_condition_is_false;
}
Нежелательно использовать простые присваивания в условном выражении, т.к. присваивание может быть спутано с равенством при быстром просмотре кода. Например, не используйте следующий код:
Если вам нужно использовать присваивание в условном выражении, то распространённой практикой является заключение операции присваивания в дополнительные скобки. Например:
if ( (x = y) ) { /* ... */ }
Ложные значения
Следующие значения являются ложными:
falseundefinednull0NaN- пустая строка (
"")
Все остальные значения, включая все объекты, будут восприняты как истина при передаче в условное выражение.
Не путайте примитивные логические значения true и false со значениями true и false объекта Boolean. Например:
var b = new Boolean(false);
if (b) // это условие true
if (b == true) // это условие false
В следующем примере функция checkData возвращает true, если число символов в объекте Text равно трём; в противном случае функция отображает окно alert и возвращает false.
function checkData() {
if (document.form1.threeChar.value.length == 3) {
return true;
} else {
alert("Enter exactly three characters. " +
document.form1.threeChar.value + " is not valid.");
return false;
}
}
Инструкция switch
Инструкция switch позволяет сравнить значение выражения с различными вариантами и при совпадении выполнить соответствующий код. Инструкция имеет следующий вид:
switch (expression) {
case label_1:
statements_1
[break;]
case label_2:
statements_2
[break;]
...
default:
statements_default
[break;]
}
Сначала производится поиск ветви case с меткой label, совпадающей со значением выражения expression. Если совпадение найдено, то соответствующий данной ветви код выполняется до оператора break, который прекращает выполнение switch и передаёт управление дальше. В противном случае управление передаётся необязательной ветви default и выполняется соответствующий ей код. Если ветвь default не найдена, то программа продолжит выполняться со строчки, следующей за инструкцией switch. По соглашению ветвь default является последней ветвью, но следовать этому соглашению необязательно.
Если оператор break отсутствует, то после выполнения кода, который соответствует выбранной ветви, начнётся выполнение кода, который следует за ней.
В следующем примере если fruittype имеет значение "Bananas", то будет выведено сообщение "Bananas are $0.48 a pound." и оператор break прекратит выполнение switch. Если бы оператор break отсутствовал, то был бы также выполнен код, соответствующий ветви "Cherries", т.е. выведено сообщение "Cherries are $3.00 a pound.".
switch (fruittype) {
case "Oranges":
console.log("Oranges are $0.59 a pound.");
break;
case "Apples":
console.log("Apples are $0.32 a pound.");
break;
case "Bananas":
console.log("Bananas are $0.48 a pound.");
break;
case "Cherries":
console.log("Cherries are $3.00 a pound.");
break;
case "Mangoes":
console.log("Mangoes are $0.56 a pound.");
break;
case "Papayas":
console.log("Mangoes and papayas are $2.79 a pound.");
break;
default:
console.log("Sorry, we are out of " + fruittype + ".");
}
console.log("Is there anything else you'd like?");
Инструкции обработки исключений
Инструкция throw используется, чтобы выбросить исключение, а инструкция try...catch, чтобы его обработать.
Типы исключений
Практически любой объект может быть выброшен как исключение. Тем не менее, не все выброшенные объекты создаются равными. Обычно числа или строки выбрасываются как исключения, но часто более эффективным является использование одного из типов исключений, специально созданных для этой цели:
- Исключения ECMAScript
DOMException(en-US) иDOMError(en-US)
Инструкция throw
Используйте инструкцию throw, чтобы выбросить исключение. При выбросе исключения нужно указать выражение, содержащее значение, которое будет выброшено:
throw expression;
Вы можете выбросить любое выражение, а не только выражения определённого типа. В следующем примере выбрасываются исключения различных типов:
throw "Error2"; // string
throw 42; // number
throw true; // boolean
throw { toString: function() { return "I'm an object!"; } }; // object
Примечание: Вы можете выбросить объект как исключение. Вы можете обращаться к свойствам данного объекта в блоке catch.
Примечание: В следующем примере объект UserException выбрасывается как исключение:
function UserException (message) {
this.message = message;
this.name = "UserException";
}
UserException.prototype.toString = function () {
return this.name + ': "' + this.message + '"';
}
throw new UserException("Value too high");
Инструкция try…catch
Инструкция try...catch состоит из блока try, который содержит одну или несколько инструкций, и блок catch, которые содержит инструкции, определяющие порядок действий при выбросе исключения в блоке try. Иными словами, если в блоке try будет выброшено исключение, то управление будет передано в блок catch. Если в блоке try не возникнет исключений, то блок catch будет пропущен. Блок finally будет выполнен после окончания работы блоков try и catch, вне зависимости от того, было ли выброшено исключение.
В следующем примере вызывается функция getMonthName, которая возвращает название месяца по его номеру. Если месяца с указанным номером не существует, то функция выбросит исключение "InvalidMonthNo", которое будет перехвачено в блоке catch:
function getMonthName(mo) {
mo = mo - 1; // Adjust month number for array index (1 = Jan, 12 = Dec)
var months = ["Jan","Feb","Mar","Apr","May","Jun","Jul",
"Aug","Sep","Oct","Nov","Dec"];
if (months[mo]) {
return months[mo];
} else {
throw "InvalidMonthNo"; //throw keyword is used here
}
}
try { // statements to try
monthName = getMonthName(myMonth); // function could throw exception
}
catch (e) {
monthName = "unknown";
logMyErrors(e); // pass exception object to error handler -> your own
}
Блок catch
Используйте блок catch, чтобы обработать исключения, сгенерированные в блоке try.
catch (catchID) { statements }
JavaScript создаёт идентификатор catchID, которому присваивается перехваченное исключение, при входе в блок catch; данный идентификатор доступен только в пределах блока catch и уничтожается при выходе из него.
В следующем примере выбрасывается исключение, которое перехватывается в блоке catch:
try {
throw "myException"
} catch (e) {
console.error(e);
}
Блок finally
Блок finally содержит код, который будет выполнен после окончания работы блоков try и catch, но до того, как будет выполнен код, который следует за инструкцией try...catch. Блок finally выполняется вне зависимости от того, было ли выброшено исключение. Блок finally выполняется даже в том случае, если исключение не перехватывается в блоке catch.
В следующем примере открывается файл, затем в блоке try происходит вызов функции writeMyFile, который может выбросить исключение. Если возникает исключение, то оно обрабатывается в блоке catch. В любом случае файл будет закрыт функцией closeMyFile, вызов которой находится в блоке finally.
openMyFile();
try {
writeMyFile(theData);
} catch(e) {
handleError(e);
} finally {
closeMyFile();
}
Если блок finally возвращает значение, то данное значение становится возвращаемым значением всей связки try-catch-finally. Значения, возвращаемые блоками try и catch, будут проигнорированы.
function f() {
try {
console.log(0);
throw "bogus";
} catch(e) {
console.log(1);
return true; // приостанавливается до завершения блока `finally`
console.log(2); // не выполняется
} finally {
console.log(3);
return false; // заменяет предыдущий `return`
console.log(4); // не выполняется
}
// `return false` выполняется сейчас
console.log(5); // не выполняется
}
f(); // отображает 0, 1, 3 и возвращает `false`
Замена возвращаемых значений блоком finally распространяется в том числе и на исключения, которые выбрасываются или перевыбрасываются в блоке catch:
function f() {
try {
throw "bogus";
} catch(e) {
console.log('caught inner "bogus"');
throw e; // приостанавливается до завершения блока `finally`
} finally {
return false; // заменяет предыдущий `throw`
}
// `return false` выполняется сейчас
}
try {
f();
} catch(e) {
// Не выполняется, т.к. `throw` в `catch `заменяется на `return` в `finally`
console.log('caught outer "bogus"');
}
// В результате отображается сообщение caught inner "bogus"
// и возвращается значение `false`
Вложенные инструкции try...catch
Вы можете вкладывать инструкции try...catch друг в друга. Если внутренняя инструкция try...catch не имеет блока catch, то она должна иметь блок finally, кроме того исключение будет перехвачено во внешнем блоке catch. Для получения большей информации ознакомьтесь с вложенными try-блоками.
Использование объекта Error
В зависимости от типа ошибки вы можете использовать свойства name и message, чтобы получить более подробную информацию. Свойство name содержит название ошибки (например, DOMException или Error), свойство message — описание ошибки.
Если вы выбрасываете собственные исключения, то чтобы получить преимущество, которое предоставляют эти свойства (например, если ваш блок catch не делает различий между вашими исключениями и системными), используйте конструктор Error. Например:
function doSomethingErrorProne () {
if ( ourCodeMakesAMistake() ) {
throw ( new Error('The message') );
} else {
doSomethingToGetAJavascriptError();
}
}
try {
doSomethingErrorProne();
} catch (e) {
console.log(e.name); // 'Error'
console.log(e.message); // 'The message' или JavaScript error message
}
Объект Promise
Начиная с ECMAScript2015, JavaScript поддерживает объект Promise, который используется для отложенных и асинхронных операций.
Объект Promise может находиться в следующих состояниях:
- ожидание (pending): начальное состояние, не выполнено и не отклонено.
- выполнено (fulfilled): операция завершена успешно.
- отклонено (rejected): операция завершена с ошибкой.
- заданный (settled): промис выполнен или отклонен, но не находится в состоянии ожидания.
Загрузка изображения при помощи XHR
Простой пример использования объектов Promise и XMLHttpRequest для загрузки изображения доступен в репозитории MDN promise-test на GitHub. Вы также можете посмотреть его в действии. Каждый шаг прокомментирован, что позволяет вам разобраться в архитектуре Promise и XHR. Здесь приводится версия без комментариев:
function imgLoad(url) {
return new Promise(function(resolve, reject) {
var request = new XMLHttpRequest();
request.open('GET', url);
request.responseType = 'blob';
request.onload = function() {
if (request.status === 200) {
resolve(request.response);
} else {
reject(Error('Image didn't load successfully; error code:'
+ request.statusText));
}
};
request.onerror = function() {
reject(Error('There was a network error.'));
};
request.send();
});
}
- « Предыдущая статья
- Следующая статья »
Инструкция try позволяет тестировать блок кода на наличие ошибок.
Оператор catch позволяет обработать ошибку.
Инструкция throw позволяет создавать пользовательские ошибки.
Оператор finally позволяет выполнять код после try и catch независимо от результата.
Ошибки будут происходить!
При выполнении кода JavaScript могут возникать различные ошибки.
Ошибки могут быть кодирование ошибки, сделанные программистом, ошибки из-за неправильного ввода, и другие непредвиденные вещи.
Пример
In this example we have written alert as adddlert to deliberately produce an error:
<p id=»demo»></p>
<script>
try {
adddlert(«Welcome guest!»);
}
catch(err) {
document.getElementById(«demo»).innerHTML = err.message;
}
</script>
JavaScript ловит адддлерт как ошибку и выполняет код catch для его обработки.
JavaScript попробовать и поймать
Инструкция try позволяет определить блок кода для проверки на наличие ошибок во время его выполнения.
Инструкция catch позволяет определить блок кода, который будет выполняться, если в блоке try возникает ошибка.
Операторы JavaScript try и catch поставляются парами:
try {
Block of code to try
}
catch(err) {
Block of code to handle errors
}
JavaScript бросает ошибки
При возникновении ошибки JavaScript обычно останавливается и генерирует сообщение об ошибке.
Технический термин для этого: JavaScript будет генерировать исключение (выбросить ошибку).
JavaScript фактически создаст объект Error с двумя свойствами: Name и Message.
Инструкция throw
Инструкция throw позволяет создать пользовательскую ошибку.
Технически вы можете вызвать исключение (выбросить ошибку).
Исключением может быть строка JavaScript, число, логическое значение или объект:
throw «Too big»; // throw a text
throw 500; // throw a number
Если вы используете throw вместе с try и catch, вы можете управлять потоком программы и генерировать пользовательские сообщения об ошибках.
Пример проверки ввода
В этом примере анализируется ввод. Если значение неверно, генерируется исключение (ERR).
Исключение (ERR) перехватывается оператором catch и отображается пользовательское сообщение об ошибке:
<!DOCTYPE html>
<html>
<body>
<p>Please input a number between
5 and 10:</p>
<input id=»demo» type=»text»>
<button type=»button»
onclick=»myFunction()»>Test Input</button>
<p id=»p01″></p>
<script>
function myFunction() {
var message, x;
message =
document.getElementById(«p01»);
message.innerHTML = «»;
x =
document.getElementById(«demo»).value;
try {
if(x == «») throw «empty»;
if(isNaN(x)) throw «not a number»;
x = Number(x);
if(x <5) throw
«too low»;
if(x> 10) throw «too
high»;
}
catch(err) {
message.innerHTML =
«Input is » + err;
}
}
</script>
</body>
</html>
Проверка HTML
Приведенный выше код является лишь примером.
Современные браузеры часто используют комбинацию JavaScript и встроенную проверку HTML, используя предопределенные правила проверки, определенные в атрибутах HTML:
<input id=»demo» type=»number» min=»5″ max=»10″ step=»1″
Подробнее о проверке форм можно прочитать в следующей главе этого учебного пособия.
Окончательное заявление
Оператор finally позволяет выполнять код после try и catch независимо от результата:
try {
Блок кода, чтобы попробовать
}
catch(err) {
Блок кода для обработки ошибок
}
finally {
Блок кода, который будет выполняться независимо от результата try/catch
}
Пример
function myFunction() {
var message, x;
message =
document.getElementById(«p01»);
message.innerHTML = «»;
x =
document.getElementById(«demo»).value;
try {
if(x == «») throw «is empty»;
if(isNaN(x))
throw «is not a number»;
x = Number(x);
if(x>
10) throw «is too high»;
if(x <
5) throw «is too low»;
}
catch(err)
{
message.innerHTML = «Error: » +
Объект Error
JavaScript имеет встроенный объект Error, предоставляющий сведения об ошибке при возникновении ошибки.
Объект Error предоставляет два полезных свойства: Name и Message.
Свойства объекта Error
| Свойство | Описание |
|---|---|
| name | Задает или возвращает имя ошибки |
| message | Задает или возвращает сообщение об ошибке (строка) |
Значения имени ошибки
В свойстве «имя ошибки» может быть возвращено шесть различных значений:
| Ошибки | Описание |
|---|---|
| EvalError | An error occurred in the eval () function |
| RangeError | Число «вне диапазона» произошло |
| ReferenceError | Произошла незаконная ссылка |
| SyntaxError | Произошла синтаксическая ошибка |
| TypeError | Произошла ошибка типа |
| URIError | Произошла ошибка в encodeURI () |
Шесть различных значений описаны ниже.
Ошибка eval
евалеррор указывает на ошибку в функции eval ().
Новые версии JavaScript не выбрасывают никаких евалеррор. Вместо этого используйте синтаксическая ошибка.
Ошибка диапазона
RangeError создается, если используется число, которое выходит за пределы диапазона правовых значений.
Например: нельзя задать число значащих цифр числа 500.
Пример
var num = 1;
try {
num.toPrecision(500); // A number cannot have 500
significant digits
}
catch(err) {
document.getElementById(«demo»).innerHTML = err.name;
}
Ошибка ссылки
ReferenceError создается, если используется (ссылка) переменная, которая не была объявлена:
Пример
var x;
try {
x = y + 1; // y cannot be referenced (used)
}
catch(err) {
document.getElementById(«demo»).innerHTML = err.name;
}
Синтаксическая ошибка
При попытке вычисления кода с ошибкой синтаксиса создается Синтаксическая ошибка.
Пример
try {
eval(«alert(‘Hello)»); //
Missing ‘ will produce an error
}
catch(err) {
document.getElementById(«demo»).innerHTML = err.name;
}
Ошибка типа
Если используется значение, находящиеся вне диапазона ожидаемых типов, создается TypeError.
Пример
var num = 1;
try {
num.toUpperCase(); // You cannot convert a number
to upper case
}
catch(err) {
document.getElementById(«demo»).innerHTML = err.name;
}
Ошибка URI
При использовании недопустимых символов в функции URI создается уриеррор :
Пример
try {
decodeURI(«%%%»); // You cannot URI decode
these percent signs
}
catch(err) {
document.getElementById(«demo»).innerHTML = err.name;
}
Нестандартные свойства объекта Error
Mozilla и Microsoft определяет некоторые нестандартные свойства объекта Error:
имя файла (Mozilla)
LineNumber (Mozilla)
колумннумбер (Mozilla)
стек (Mozilla)
Описание (Майкрософт)
номер (Майкрософт)
Не используйте эти свойства на общедоступных веб-узлах. Они не будут работать во всех браузерах.
В этой статье мы познакомимся с инструкцией для обработки ошибок try...catch и throw для генерирования исключений.
Непойманные ошибки
Ошибке в коде могут возникать по разным причинам. Например, вы отправили запрос на сервер, а он дал сбой и прислал ответ, который привёл к неожиданным последствиям. Кроме этой, могут быть тысячи других, а также свои собственные.
Когда возникает ошибка, выполнение кода прекращается, и эта ошибка выводится в консоль:
const json = '{name:"Александр"}';
const person = JSON.parse(json); // Uncaught SyntaxError: Unexpected token n in JSON at position 1
console.log('Это сообщение мы не увидим!');

Выполнение этого примера остановится при парсинге строки JSON. В консоль будет выведена непойманная ошибка (uncaught error). Она так называется, потому что мы её не поймали (не обработали). Дальше код выполняться не будет и сообщение, которые мы выводим с помощью console.log() не отобразится.
Обработка ошибок в JavaScript осуществляется с помощью try...catch.
try...catch – это специальный синтаксис, состоящий из 2 блоков кода:
try {
// блок кода, в котором имеется вероятность возникновения ошибки
} catch(error) {
// этот блок выполняется только в случае возникновения ошибки в блоке try
}
Первый блок идёт сразу после ключевого слова try. В этот блок мы помещаем часть кода, в котором есть вероятность возникновения ошибки.
Второй блок располагается за ключевым словом catch. В него помещаем код, который будет выполнен только в том случае, если в первом блоке возникнет ошибка. В круглых скобках после catch указываем параметр error. В этот параметр будет помещена ошибка, которая возникла в блоке try.
Код, приведённый выше мы обернули в try...catch, а именно ту его часть, в котором может возникнуть ошибка:
const text = '{name:"Александр"}';
try {
const person = JSON.parse(text); // Uncaught SyntaxError: Unexpected token n in JSON at position 1
} catch(error) {
console.error(error);
console.log(error.message);
}
console.log('Это сообщение мы увидим!');

Здесь в блоке try произойдет ошибка, так как в данном примере мы специально присвоили переменной text некорректную строку JSON. В catch эта ошибка будет присвоена параметру error, и в нём мы будем просто выводить эту ошибку в консоль с помощью console.error(). Таким образом она будет выведена также красным цветом, но без слова Uncaught, т.к. эта ошибка была поймана.
Ошибка – это объект и у него имеются следующие свойства:
message– описание ошибки;name– тип ошибки, например, RangeError при указании значения выходящего за пределы диапазона;stack– строка стека, которая используется в целях отладки; она позволяет узнать о том, что происходило в скрипте на момент возникновения ошибки.
В этом примере мы также написали инструкцию для вывода описание ошибки error.message в консоль с помощью console.log().
Пример функции для проверки корректности JSON:
const isValidJSON = (text) => {
try {
JSON.parse(text);
return true;
} catch {
return false;
}
}

При вызове функции, сначала будет выполняться инструкция JSON.parse(text). Если ошибки не возникнет, то возвратится значение true. В противном случае, интерпретатор перейдёт в секцию catch. В итоге будет возвращено false. Кстати здесь catch записан без указания круглых скобок и параметра внутри них. Эта возможность была добавлена в язык, начиная с версии ECMAScript 2019.
Блок «finally»
В JavaScript возможны три формы инструкции try:
try...catchtry...finallytry...catch...finally
Блок finally выполняется всегда, независимо от того возникли ошибки в try или нет. Он выполняется после try, если ошибок не было, и после catch, если ошибки были. Секция finally не имеет параметров.
Пример с использованием finally:
let result = 0;
try {
result = sum(10, 20);
console.log('Это сообщение мы не увидим!');
} catch(error) {
console.log(error.message);
} finally {
console.log(result);
}

В этом примере произойдет ошибка в секции try, так как sum нигде не определена. После возникновения ошибки интерпретатор перейдём в catch. Здесь с помощью метода console.log() сообщение об ошибке будет выведено в консоль. Затем выполнится инструкция, находящаяся в блоке finally.
В JavaScript имеется также конструкция без catch:
try {
// ...
} finally {
// завершаем какие-то действия
}
Инструкция throw
В JavaScript имеется инструкция throw, которая позволяет генерировать ошибку.
Синтаксис инструкции throw:
throw expression;
Как правило, в качестве выражения обычно используют встроенный основной класс для ошибок Error или более конкретный, например: RangeError, ReferenceError, SyntaxError, TypeError, URIError или другой.
Создаём новый объект Error и выбрасываем его в качестве исключения:
throw new Error('Какое-то описание ошибки');
Пример генерирования синтаксической ошибки:
throw new SyntaxError('Описание ошибки');
В качестве выражения можно использовать не только объект ошибки, но и строки, числа, логические значения и другие величины. Но делать это не рекомендуется:
throw 'Значение не является числом';
При обнаружении оператора throw выполнение кода прекращается, и ошибка выбрасывается в консоль.
Например, создадим функцию, которая будет просто выбрасывать новую ошибку:
// создаём стрелочную функцию и присваиваем её переменной myFn
const myFn = () => {
throw new Error('Описание ошибки');
}
// вызываем функцию
myFn();
console.log('Это сообщение мы не увидим в консоли!');

Для обработки ошибки обернём вызов функции в try...catch:
const myFn = () => {
throw new Error('Описание ошибки');
}
try {
myFn();
} catch(error) {
console.error(error);
}
console.log('Это сообщение мы увидим в консоли!');

В этом примере вы увидите в консоли ошибку и дальше сообщение, которые мы выводим с помощью console.log(). То есть выполнение кода продолжится.
Кроме встроенных классов ошибок можно создать свои собственные, например, путем расширения Error:
class FormError extends Error {
constructor(message) {
super(message);
this.name = 'FormError';
}
}
Использование своего класса FormError для отображение ошибок формы:
<form novalidate>
<input type="text" name="name" required>
<input type="email" name="email" required>
<button type="submit">Отправить</button>
</form>
<script>
class FormError extends Error {
constructor(message) {
super(message);
this.name = 'FormError';
}
}
const elForm = document.querySelector('form');
elForm.onsubmit = (e) => {
e.preventDefault();
elForm.querySelectorAll('input').forEach((el) => {
if (!el.checkValidity()) {
try {
throw new FormError(`[name="${el.name}"] ${el.validationMessage}`);
} catch(error) {
console.error(`${error.name} ${error.message}`);
}
}
});
}
</script>

Глобальная ловля ошибок
Возникновение ошибок, которые мы никак не обрабатываем с помощью try, можно очень просто перехватить посредством window.onerror:
window.onerror = function(message, source, lineno, colno, error) {
// ...
}
Это анонимное функциональное выражение будет вызываться каждый раз при возникновении непойманной ошибки. Ей передаются аргументы, которые мы будем получать с помощью следующих параметров:
message— строка, содержащее сообщение об ошибке;source— URL-адрес скрипта или документа, в котором произошла ошибка;linenoиcolno— соответственно номер строки и столбца, в которой произошла ошибка;error— объект ошибки илиnull, если соответствующий объект ошибки недоступен;
Передача ошибок на сервер
Что делать с этими ошибками? Их, например, можно передавать на сервер для того чтобы позже можно было проанализировать эти ошибки и принять меры по их устранению.
Пример кода для отправки ошибок, возникающих в браузере на сервер через AJAX с использованием fetch:
window.onerror = (message, source, lineno, colno) => {
const err = { message, source, lineno, colno };
fetch('/assets/php/error-log.php', {
method: 'post',
body: JSON.stringify(err)
});
}
На сервере, если, например, сайт на PHP, можно написать такой простенький скрипт:
<?php
define('LOG_FILE', 'logs/' . date('Y-m-d') . '.log');
$json = file_get_contents('php://input');
$data = json_decode($json, true);
try {
error_log('[' . date('d.m.Y h:i:s') . '] [' . $data['message'] . '] [' . $data['lineno'] . ', ' . $data['colno'] . '] [' . $data['source'] . '] [' . $_SERVER['HTTP_USER_AGENT'] . ']' . PHP_EOL, 3, LOG_FILE);
} catch(Exception $e) {
$message = implode('; ', $data);
error_log('[' . date('d.m.Y h:i:s') . '] [' . $message . '] [' . $_SERVER['HTTP_USER_AGENT'] . ']' . PHP_EOL, 3, LOG_FILE);
}
Его следует сохранить в файл /assets/php/error-log.php, а также в этом каталоге создать папку logs для сохранения в ней логов.
В результате когда на клиенте, то есть в браузере будет возникать JavaScript ошибки, они будут сохраняться на сервер в файл следующим образом: