Использование throw и конструкции try … catch … finally в Javascript

Владислав Белецкий
Владислав Белецкий .
Категория:
Комментариев: 0

В любом коде возможны ошибки. В случае, когда такая ошибка встречается в JavaScript, интерпретатор прекращает выполнение кода и выводит в консоль браузера сообщение об ошибке. Для того чтобы ваш код на JS оставался работоспособным, эти ошибки имеет смысл перехватывать.

Для перехвата ошибки необходимо возбудить, или “выбросить” исключение.  В терминах программирования за это отвечает инструкция  throw – она нужна для описания того, что будет делать JavaScript, когда он встречает что-то, что не может быть обработано. Используя инструкцию throw, вы технически генерируете исключение. Фактически после ключевого слова throw мы должны вывести какое-то сообщение об ошибке, которое дальше нужно будет как-то обработать, например, вывести в console.error().

В документации на MDN об инструкции throw написано следующее:

Инструкция throw позволяет генерировать исключения, определяемые пользователем. При этом выполнение текущей функции будет остановлено (инструкции после throw не будут выполнены), и управление будет передано в первый блок catch в стеке вызовов. Если catch блоков среди вызванных функций нет, выполнение программы будет остановлено.

Генерация исключения с помощью throw

Давайте попробуем использовать генерацию исключения в примере с назначением через JavaScript стилевых свойств для абзаца с id="testBlock". Поскольку все доступные для html-элементов стилевые свойства известны браузеру, мы будем их проверять с помощью оператора in, который возвращает true, если свойство содержится в указанном объекте. В том случае, если мы вызываем в функции setStyle() существующее свойство, оно применится к абзацу. Если нет, то будет выброшено исключение, которое с помощью инструкции throw выведет ошибку в консоль браузера.

Назначьте стили для абзаца, нажав на кнопку:

Тестируем стилевые свойства параграфа

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, синтаксис которой будет таким:

Чаще всего в блоке try используется инструкция throw в случае, если код может вызвать ошибку, а в блоке catch эта ошибка как-то выводится или обрабатывается.

Варианты использования конструкции try … catch … finally

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

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

Если перевести с английского инструкции, заданные ключевыми словами try ... catch ... finally, то в первом блоке мы будем пытаться (try) выполнить некий код, в блоке catch мы будем “ловить” ошибки, а в блок finally сработает  в любом случае в самом конце, но только после того, как будет выполнен код в одном из предыдущих блоков.

В случае успешного выполнения кода в блоке try на этом все может закончиться, если отсутствует блок finally. Т. е. блок catch не выполняется, поскольку ошибки нет.

Инструкции (операторы) в блоке catch будут выполнены, если в блоке try произошла ошибка. Когда любой код в блоке try выбрасывает исключение, то управление сразу же переходит в блок catch.

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

Например, мы создаем простую функцию суммы, в которой складываем 2 числа и возвращаем результат сложения.  Однако, если в такую функцию будет передано не число, а строка, то произойдет конкатенация строк и результат сложения будет некорректным. Давайте попробуем сообщить об этом пользователю, используя конструкцию try ... catch:

В результате первого вызова функции мы получим в диалоговом окне alert() сообщение “Введены некорректные значения”, а во втором случае число 47.

Если добавить в эту функцию блок finally и несколько переписать сам код, то мы получим совсем другой результат. Здесь мы будем использовать для проверки на числовой тип не оператор typeof, а функцию isNaN(), а также метод parseFloat() для преобразования строки в число. Этот метод умеет отсекать строки от чисел, если строка следует за числом, но не наоборот.

Обратите внимание, что  строка, подсчитывающая сумму с учетом преобразования строки в число методом parseFloat() “переехала” в блок finally, поэтому сообщение об ошибке, которое выводится в console.error() мы видим, но в alert() получаем NaN из блока finally, а не сообщение “Введены некорректные значения” из блока catch.

Практическое применение try … catch в функции, определяющей расширение файла

Давайте рассмотрим чуть более сложную задачу, в которой нам нужно определить расширение файла в строке, передаваемой в функцию. Внутри этой функции мы попытаемся найти в строке методом lastIndexOf('.') символ точки. Если он найден, то мы выведем это расширение, если символ точки отсутствует или есть только в конце строки, то мы выводим информацию о том, что у файла нет расширения. В том же случае, когда функция вызывается с параметром в виде числа, сработает код в блоке catch, и мы получим сообщение об ошибке и вывод текста “неверный формат”.

Попробуйте сами:

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()  с отложенным временем выполнения в виде задержки в миллисекундах.

Например:

Попробуйте сами:

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(), зато в консоли обнаружите ошибку в виде Uncaught Error: Ошибка в консоли! Неверный ввод!

Использование конструкции try…catch…finally  при асинхронной загрузке данных в функциях с async/await

На данный момент для загрузки данных становится все более популярным использование async/await функций. Однако и в них могут происходить ошибки. Поэтому вы можете использовать блоки try ... catch для обработки ошибок в асинхронных функциях.

Например, у нас есть необходимость получать асинхронно какую-либо информацию из JSON-файла. Мы будем использовать возможность загрузки фейкового контента с сервиса JSONPlaceholder. В функции fetchTodos() будем загружать “список дел” для виртуальных пользователей.

Измените адрес для загрузки контента в 4-й строке – и увидите ошибку в  div с id="wrap".

See the Pen Random Quote Machine by Elen (@ambassador)
on CodePen.0

Подписаться
Уведомить о
guest
0 комментариев
Межтекстовые Отзывы
Посмотреть все комментарии