В любом коде возможны ошибки. В случае, когда такая ошибка встречается в JavaScript, интерпретатор прекращает выполнение кода и выводит в консоль браузера сообщение об ошибке. Для того чтобы ваш код на JS оставался работоспособным, эти ошибки имеет смысл перехватывать.
Для перехвата ошибки необходимо возбудить, или “выбросить” исключение. В терминах программирования за это отвечает инструкция throw – она нужна для описания того, что будет делать JavaScript, когда он встречает что-то, что не может быть обработано. Используя инструкцию throw
, вы технически генерируете исключение. Фактически после ключевого слова throw
мы должны вывести какое-то сообщение об ошибке, которое дальше нужно будет как-то обработать, например, вывести в console.error()
.
В документации на MDN об инструкции throw
написано следующее:
Инструкция
throw
позволяет генерировать исключения, определяемые пользователем. При этом выполнение текущей функции будет остановлено (инструкции послеthrow
не будут выполнены), и управление будет передано в первый блокcatch
в стеке вызовов. Еслиcatch
блоков среди вызванных функций нет, выполнение программы будет остановлено.
Генерация исключения с помощью throw
Давайте попробуем использовать генерацию исключения в примере с назначением через JavaScript стилевых свойств для абзаца с id="testBlock"
. Поскольку все доступные для html-элементов стилевые свойства известны браузеру, мы будем их проверять с помощью оператора in
, который возвращает true
, если свойство содержится в указанном объекте. В том случае, если мы вызываем в функции setStyle()
существующее свойство, оно применится к абзацу. Если нет, то будет выброшено исключение, которое с помощью инструкции throw
выведет ошибку в консоль браузера.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
function setStyle(str, value) {
if (str in testBlock.style) {
testBlock.style[str] = value;
} else {
throw new Error(“Переданная строка не является CSS-свойством”);
}
}
//console.log(testBlock.style); // вы можете посмотреть все доступные стилевые свойства для html-элемента
setStyle(“color”, ‘green’);
setStyle(“background-color”, ‘lime’);
setStyle(“margin”, ’20px’);
setStyle(“font-size”, ’24px’);
setStyle(“next-style”, ’40vh’);// несуществующее свойство – ошибка
setStyle(“text-transform”, ‘uppercase’); //не выводится
|
Назначьте стили для абзаца, нажав на кнопку:
Тестируем стилевые свойства параграфа
function trySetStyle(){
function setStyle(str, value) {
if (str in testBlock.style) {
testBlock.style[str] = value;
} else {
throw new TypeError(“Переданная строка не является CSS-свойством”);
}
}
//console.log(testBlock.style);
setStyle(“color”, ‘green’);
setStyle(“background-color”, ‘lime’);
setStyle(“margin”, ’20px’);
setStyle(“font-size”, ’24px’);
setStyle(“next-style”, ’40vh’);
setStyle(“text-transform”, ‘uppercase’);
}
Если вы проанализируете, какие стили были назначены, то увидите, что после ошибочного стиля, который мы пытались назначить строкой setStyle("next-style", '40vh')
, последний существующий стиль из строки setStyle("text-transform", 'uppercase')
уже не применился, т.к. была выдана ошибка, сгенерированная инструкцией throw
, и дальнейшее выполнение кода было остановлено.
В консоли вы увидите текст Uncaught Error: Переданная строка не является CSS-свойством.
В нашем случае Uncaught Error – это неперехваченная ошибка, которая не относится ни к одному из стандартных типов ошибок объекта Error в JavaScript. Обычно интерпретатор JavaScript возбуждает исключения с именами ‘SyntaxError’, ‘TypeError’, ‘RangeError’, ‘ReferenceError’, ‘URIError’ или ‘InternalError’.
Перехват ошибок в конструкции try ... catch ... finally
Для того чтобы перехватить возникающие ошибки в коде, в JavaScript существует конструкция try ... catch ... finally
, синтаксис которой будет таким:
1
2
3
4
5
6
7
8
9
10
|
try {
оператор 1;
оператор 2;
...
оператор n;
} catch {
обработчик ошибки/исключения;
} finally {
финальный код;
}
|
Чаще всего в блоке try
используется инструкция throw
в случае, если код может вызвать ошибку, а в блоке catch
эта ошибка как-то выводится или обрабатывается.
Варианты использования конструкции try … catch … finally
Конструкция try ... catch ... finally
всегда начинается с блока try
, в котором находится одна или несколько инструкций (операторов). В ряде случаев там может быть всего одна строка, например, вызов какой-либо функции. А вот после блока try
может идти блок catch
или блок finally
. То есть вы можете использовать один из вариантов конструкции try ... catch ... finally
:
try {...} catch {...}
try {...} finally {...}
try {...} catch {...} finally {...}
Если перевести с английского инструкции, заданные ключевыми словами try ... catch ... finally
, то в первом блоке мы будем пытаться (try
) выполнить некий код, в блоке catch
мы будем “ловить” ошибки, а в блок finally
сработает в любом случае в самом конце, но только после того, как будет выполнен код в одном из предыдущих блоков.
В случае успешного выполнения кода в блоке try
на этом все может закончиться, если отсутствует блок finally
. Т. е. блок catch
не выполняется, поскольку ошибки нет.
Инструкции (операторы) в блоке catch
будут выполнены, если в блоке try
произошла ошибка. Когда любой код в блоке try
выбрасывает исключение, то управление сразу же переходит в блок catch
.
Блок finally
выполнится после выполнения блоков try
и catch
, но перед любым кодом, который идет после конструкции try...catch
. Он выполняется всегда, независимо от того, было исключение (ошибка) или нет.
Например, мы создаем простую функцию суммы, в которой складываем 2 числа и возвращаем результат сложения. Однако, если в такую функцию будет передано не число, а строка, то произойдет конкатенация строк и результат сложения будет некорректным. Давайте попробуем сообщить об этом пользователю, используя конструкцию try ... catch
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
function summa(a, b){
try {
if (typeof a !== “number”) {
throw `Ошибка: ${a} не является числом`;
}
if (typeof b !== “number”) {
throw `Ошибка: ${b} не является числом`;
}
return a+b;
} catch (err) {
console.error(err);
return “Введены некорректные значения”;
}
}
alert(summa(10, “25”)); //”Введены некорректные значения”
alert(summa(2,45)) // 47
|
В результате первого вызова функции мы получим в диалоговом окне alert() сообщение “Введены некорректные значения”, а во втором случае число 47.
Если добавить в эту функцию блок finally
и несколько переписать сам код, то мы получим совсем другой результат. Здесь мы будем использовать для проверки на числовой тип не оператор typeof
, а функцию isNaN()
, а также метод parseFloat()
для преобразования строки в число. Этот метод умеет отсекать строки от чисел, если строка следует за числом, но не наоборот.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
function summa1(a, b){
try {
if ( isNaN( parseFloat(a) )) {
throw `Ошибка: ${a} не является числом`;
}
if ( isNaN( parseFloat(b) ) ) {
throw `Ошибка: ${b} не является числом`;
}
} catch (err) {
console.error(err);
return “Введены некорректные значения”;
} finally {
return parseFloat(a) + parseFloat(b);
}
}
alert(summa1(10, 145)); //155
alert(summa1(10, “25”)); //35
alert(summa1(10,“82.5abc”)); // 92.5
alert(summa1(10,“abc39”)); //NaN
|
Обратите внимание, что строка, подсчитывающая сумму с учетом преобразования строки в число методом parseFloat()
“переехала” в блок finally
, поэтому сообщение об ошибке, которое выводится в console.error() мы видим, но в alert()
получаем NaN
из блока finally
, а не сообщение “Введены некорректные значения” из блока catch
.
Практическое применение try … catch в функции, определяющей расширение файла
Давайте рассмотрим чуть более сложную задачу, в которой нам нужно определить расширение файла в строке, передаваемой в функцию. Внутри этой функции мы попытаемся найти в строке методом lastIndexOf('.')
символ точки. Если он найден, то мы выведем это расширение, если символ точки отсутствует или есть только в конце строки, то мы выводим информацию о том, что у файла нет расширения. В том же случае, когда функция вызывается с параметром в виде числа, сработает код в блоке catch
, и мы получим сообщение об ошибке и вывод текста “неверный формат”.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
function fileExtension(str) {
let ext = ”;
try {
let pos = str.lastIndexOf(‘.’);
if (pos > –1 && pos!=str.length–1) ext = ” расширение “ + str.slice(pos + 1);
else ext = ” нет раширения”;
return ext;
} catch (err) {
alert(err.name + “: “ + err.message);
return ” неверный формат”;
}
}
document.write(‘У файла <strong>document.docx</strong> ‘ + fileExtension(‘document.docx’) + ‘<br>’);
document.write(‘У файла <strong>page-about.html</strong> ‘ + fileExtension(‘page-about.html’) + ‘<br>’);
document.write(‘У файла <strong>Это просто предложение, без названия файла, но с точкой в конце.</strong> ‘ + fileExtension(‘Это просто предложение, без названия файла, но с точкой в конце.’) + ‘<br>’);
document.write(‘У файла <strong>document_docx</strong> ‘ + fileExtension(‘document_docx’) + ‘<br>’);
document.write(‘У файла <strong>123</strong> ‘ + fileExtension(123) + ‘<br>’);
|
Попробуйте сами:
function checkExt(){
function fileExtension(str) {
let ext = ”;
try {
let pos = str.lastIndexOf(‘.’);
if (pos > -1&& pos!=str.length-1) ext = ” расширение ” + str.slice(pos + 1) +”;
else ext = ” нет раширения”;
return ext;
} catch (err) {
alert(err.name + “: ” + err.message);
return ” неверный формат”;
}
}
outputInfo.innerHTML = ‘
У файла document.docx ‘ + fileExtension(‘document.docx’) + ”;
outputInfo.innerHTML += ‘
У файла page-about.html ‘ + fileExtension(‘page-about.html’) + ”;
outputInfo.innerHTML += ‘
У файла Это просто предложение, без названия файла, но с точкой в конце. ‘ + fileExtension(‘Это просто предложение, без названия файла, но с точкой в конце.’) + ”;
outputInfo.innerHTML += ‘
У файла document_docx ‘ + fileExtension(‘document_docx’) + ”;
outputInfo.innerHTML += ‘
У файла 123 ‘ + fileExtension(123) + ”;
}
Используем try…catch для парсинга JSON-формата
Как правило, мы получаем из JSON-файла строку и преобразуем ее в объект для дальнейшего использования. В том случае, когда вызов метода JSON.parse()
приводит к ошибке, мы генерируем исключение и обрабатываем эту ситуацию в ветви catch
:
See the Pen Untitled by Elen (@ambassador) on CodePen.0
В примере выше мы не загружаем JSON-файл, а проверяем обычный объект и объект, преобразованный в формат JSON методом JSON.stringify()
, а затем выводим данные об ошибке (о том, что получена некорректная JSON-строка) или выводим содержимое JSON.parse()
.
try..catch
не поймает ошибку, которая произойдёт в коде, записанном в виде функции в таких методах, как setTimeout()
или setInterval()
с отложенным временем выполнения в виде задержки в миллисекундах.Например:
1
2
3
4
5
6
7
8
9
|
let num = +prompt(‘Введите число от 1 до 10’);
try {
setTimeout(function() {
if(num<1 || num>10 || isNaN(num)) throw new Error(‘Ошибка в консоли! Неверный ввод!’);
else alert(“Вы ввели число “+num);
}, 500);
} catch (e) {
alert( “Ошибка в блоке catch” );
}
|
Попробуйте сами:
function timeFunc(){
let num = +prompt(‘Введите число от 1 до 10’);
try {
setTimeout(function() {
if(num10 || isNaN(num)) throw new Error(‘Ошибка в консоли! Неверный ввод!’);
else alert(“Вы ввели число “+num);
}, 500);
} catch (e) {
alert( “Ошибка в блоке catch” );
}
}
Если вы введете число больше 10, то не увидите никакой ошибки в виде диалогового окна alert()
, зато в консоли обнаружите ошибку в виде
Использование конструкции try…catch…finally при асинхронной загрузке данных в функциях с async/await
На данный момент для загрузки данных становится все более популярным использование async/await функций. Однако и в них могут происходить ошибки. Поэтому вы можете использовать блоки try ... catch
для обработки ошибок в асинхронных функциях.
Например, у нас есть необходимость получать асинхронно какую-либо информацию из JSON-файла. Мы будем использовать возможность загрузки фейкового контента с сервиса JSONPlaceholder. В функции fetchTodos()
будем загружать “список дел” для виртуальных пользователей.
1
2
3
4
5
6
7
8
9
10
11
12
|
async function fetchTodos() {
try{
const response = await fetch(“https://jsonplaceholder.typicode.com/todos/”);
const todos = await response.json();
} catch (err){
//обработка ошибки
console.log(err);
} finally {
//вывод информации
}
}
fetchTodos();
|
Измените адрес для загрузки контента в 4-й строке – и увидите ошибку в div с id="wrap"
.
See the Pen Random Quote Machine by Elen (@ambassador)
on CodePen.0