Перехват ошибок, «try..catch»
Материал на этой странице устарел, поэтому скрыт из оглавления сайта.
Более новая информация по этой теме находится на странице https://learn.javascript.ru/try-catch.
Как бы мы хорошо ни программировали, в коде бывают ошибки. Или, как их иначе называют, «исключительные ситуации» (исключения).
Обычно скрипт при ошибке, как говорят, «падает», с выводом ошибки в консоль.
Но бывают случаи, когда нам хотелось бы как-то контролировать ситуацию, чтобы скрипт не просто «упал», а сделал что-то разумное.
Для этого в JavaScript есть замечательная конструкция 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) catch(e) < alert('Блок catch не получит управление, так как нет ошибок'); // (3) >alert("Потом код продолжит выполнение. ");
try < alert('Начало блока try'); // (1) catch(e) < alert('Ошибка ' + e.name + ":" + e.message + "\n" + e.stack); // (3) alert("Потом код продолжит выполнение. ");
try..catch подразумевает, что код синтаксически верен
Если грубо нарушена структура кода, например не закрыта фигурная скобка или где-то стоит лишняя запятая, то никакой try..catch здесь не поможет. Такие ошибки называются синтаксическими, интерпретатор не может понять такой код.
Здесь же мы рассматриваем ошибки семантические, то есть происходящие в корректном коде, в процессе выполнения.
try..catch работает только в синхронном коде
Ошибку, которая произойдёт в коде, запланированном «на будущее», например в setTimeout , try..catch не поймает:
try < setTimeout(function() < throw new Error(); // вылетит в консоль >, 1000); > catch (e)
На момент запуска функции, назначенной через setTimeout , этот код уже завершится, интерпретатор выйдет из блока try..catch .
Чтобы поймать ошибку внутри функции из setTimeout , и try..catch должен быть в той же функции.
Объект ошибки
В примере выше мы видим объект ошибки. У него есть три основных свойства:
name Тип ошибки. Например, при обращении к несуществующей переменной: "ReferenceError" . message Текстовое сообщение о деталях ошибки. stack Везде, кроме IE8-, есть также свойство stack , которое содержит строку с информацией о последовательности вызовов, которая привела к ошибке.
В зависимости от браузера у него могут быть и дополнительные свойства, см. Error в MDN и Error в MSDN.
Пример использования
В JavaScript есть встроенный метод JSON.parse(str), который используется для чтения JavaScript-объектов (и не только) из строки.
Обычно он используется для того, чтобы обрабатывать данные, полученные по сети, с сервера или из другого источника.
Мы получаем их и вызываем метод JSON.parse , вот так:
var data = ''; // строка с данными, полученная с сервера var user = JSON.parse(data); // преобразовали строку в объект // теперь user -- это JS-объект с данными из строки alert( user.name ); // Вася alert( user.age ); // 30
Более детально формат JSON разобран в главе Формат JSON, метод toJSON.
В случае, если данные некорректны, JSON.parse генерирует ошибку, то есть скрипт «упадёт».
Устроит ли нас такое поведение? Конечно нет!
Получается, что если вдруг что-то не так с данными, то посетитель никогда (если, конечно, не откроет консоль) об этом не узнает.
А люди очень-очень не любят, когда что-то «просто падает», без всякого объявления об ошибке.
Бывают ситуации, когда без try..catch не обойтись, это – одна из таких.
Используем try..catch , чтобы обработать некорректный ответ:
var data = "Has Error"; // в данных ошибка try < var user = JSON.parse(data); // catch (e) < // . выполнится catch alert( "Извините, в данных ошибка, мы попробуем получить их ещё раз" ); alert( e.name ); alert( e.message ); >
Здесь в alert только выводится сообщение, но область применения гораздо шире: можно повторять запрос, можно предлагать посетителю использовать альтернативный способ, можно отсылать информацию об ошибке на сервер… Свобода действий.
Генерация своих ошибок
Представим на минуту, что данные являются корректным JSON… Но в этом объекте нет нужного свойства name :
var data = '< "age": 30 >'; // данные неполны try < var user = JSON.parse(data); // catch (e) < // не выполнится alert( "Извините, в данных ошибка" ); >
Вызов JSON.parse выполнится без ошибок, но ошибка в данных есть. И, так как свойство name обязательно должно быть, то для нас это такие же некорректные данные, как и "Has Error" .
Для того, чтобы унифицировать и объединить обработку ошибок парсинга и ошибок в структуре, мы воспользуемся оператором throw .
Оператор throw
Оператор throw генерирует ошибку.
Технически в качестве объекта ошибки можно передать что угодно, это может быть даже не объект, а число или строка, но всё же лучше, чтобы это был объект, желательно – совместимый со стандартным, то есть чтобы у него были как минимум свойства name и message .
В качестве конструктора ошибок можно использовать встроенный конструктор: new Error(message) или любой другой.
В JavaScript встроен ряд конструкторов для стандартных ошибок: SyntaxError , ReferenceError , RangeError и некоторые другие. Можно использовать и их, но только чтобы не было путаницы.
В данном случае мы используем конструктор new SyntaxError(message) . Он создаёт ошибку того же типа, что и JSON.parse .
var data = '< "age": 30 >'; // данные неполны try < var user = JSON.parse(data); // alert( user.name ); > catch (e)
Получилось, что блок catch – единое место для обработки ошибок во всех случаях: когда ошибка выявляется при JSON.parse или позже.
Проброс исключения
В коде выше мы предусмотрели обработку ошибок, которые возникают при некорректных данных. Но может ли быть так, что возникнет какая-то другая ошибка?
Конечно, может! Код – это вообще мешок с ошибками, бывает даже так, что библиотеку выкладывают в открытый доступ, она там 10 лет лежит, её смотрят миллионы людей и на 11-й год находятся опаснейшие ошибки. Такова жизнь, таковы люди.
Блок catch в нашем примере предназначен для обработки ошибок, возникающих при некорректных данных. Если же в него попала какая-то другая ошибка, то вывод сообщения о «некорректных данных» будет дезинформацией посетителя.
Ошибку, о которой catch не знает, он не должен обрабатывать.
Такая техника называется «проброс исключения»: в catch(e) мы анализируем объект ошибки, и если он нам не подходит, то делаем throw e .
При этом ошибка «выпадает» из try..catch наружу. Далее она может быть поймана либо внешним блоком try..catch (если есть), либо «повалит» скрипт.
В примере ниже catch обрабатывает только ошибки SyntaxError , а остальные – выбрасывает дальше:
var data = '< "name": "Вася", "age": 30 >'; // данные корректны try < var user = JSON.parse(data); if (!user.name) < throw new SyntaxError("Ошибка в данных"); >blabla(); // произошла непредусмотренная ошибка alert( user.name ); > catch (e) < if (e.name == "SyntaxError") < alert( "Извините, в данных ошибка" ); >else < throw e; >>
Заметим, что ошибка, которая возникла внутри блока catch , «выпадает» наружу, как если бы была в обычном коде.
В следующем примере такие ошибки обрабатываются ещё одним, «более внешним» try..catch :
function readData() < var data = '< "name": "Вася", "age": 30 >'; try < // . blabla(); // ошибка! >catch (e) < // . if (e.name != 'SyntaxError') < throw e; // пробрасываем >> > try < readData(); >catch (e) < alert( "Поймал во внешнем catch: " + e ); // ловим >
В примере выше try..catch внутри readData умеет обрабатывать только SyntaxError , а внешний – все ошибки.
Без внешнего проброшенная ошибка «вывалилась» бы в консоль с остановкой скрипта.
Оборачивание исключений
И, для полноты картины – последняя, самая продвинутая техника по работе с ошибками. Она, впрочем, является стандартной практикой во многих объектно-ориентированных языках.
Цель функции readData в примере выше – прочитать данные. При чтении могут возникать разные ошибки, не только SyntaxError , но и, возможно, к примеру URIError (неправильное применение функций работы с URI) да и другие.
Код, который вызвал readData , хотел бы иметь либо результат, либо информацию об ошибке.
При этом очень важным является вопрос: обязан ли этот внешний код знать о всевозможных типах ошибок, которые могут возникать при чтении данных, и уметь перехватывать их?
Обычно внешний код хотел бы работать «на уровень выше», и получать либо результат, либо «ошибку чтения данных», при этом какая именно ошибка произошла – ему неважно. Ну, или, если будет важно, то хотелось бы иметь возможность это узнать, но обычно не требуется.
Это важнейший общий подход к проектированию – каждый участок функциональности должен получать информацию на том уровне, который ей необходим.
Мы его видим везде в грамотно построенном коде, но не всегда отдаём себе в этом отчёт.
В данном случае, если при чтении данных происходит ошибка, то мы будем генерировать её в виде объекта ReadError , с соответствующим сообщением. А «исходную» ошибку на всякий случай тоже сохраним, присвоим в свойство cause (англ. – причина).
Выглядит это так:
function ReadError(message, cause) < this.message = message; this.cause = cause; this.name = 'ReadError'; this.stack = cause.stack; >function readData() < var data = '< bad data >'; try < // . JSON.parse(data); // . >catch (e) < // . if (e.name == 'URIError') < throw new ReadError("Ошибка в URI", e); >else if (e.name == 'SyntaxError') < throw new ReadError("Синтаксическая ошибка в данных", e); >else < throw e; // пробрасываем >> > try < readData(); >catch (e) < if (e.name == 'ReadError') < alert( e.message ); alert( e.cause ); // оригинальная ошибка-причина >else < throw e; >>
Этот подход называют «оборачиванием» исключения, поскольку мы берём ошибки «более низкого уровня» и «заворачиваем» их в ReadError , которая соответствует текущей задаче.
Секция finally
Конструкция try..catch может содержать ещё один блок: finally .
Выглядит этот расширенный синтаксис так:
try < .. пробуем выполнить код .. >catch(e) < .. перехватываем исключение .. >finally
Секция finally не обязательна, но если она есть, то она выполняется всегда:
- после блока try , если ошибок не было,
- после catch , если они были.
Попробуйте запустить такой код?
как проверить javascript в браузере
Также, существует онлайн-сервис Runit для работы и проверки работы кода Javascript.
Данный IDE предоставляет следующие возможности:
- Редактор полностью запускается в браузере, поэтому можно писать код в считанные секунды
- Работа с любого компьютера, имеющего доступ в интернет
- Использование редактора на macOS, Windows, Linux или любой другой ОС
- Без траты времени на настройку среды
- Можно делится своим кодом с другими участниками
13 апреля 2023
Для проверки JavaScript в браузере необходимо выполнить следующие шаги:
- Открыть веб-браузер на компьютере;
- Открыть любой сайт, на котором можно проверить JavaScript, например, https://jsfiddle.net/ ;
- Нажать на кнопку "Новый" (или "New"), чтобы создать новый файл;
- В открывшемся окне в разделе "JS" написать код на JavaScript, который вы хотите проверить;
- Нажать на кнопку "Выполнить" (или "Run"), чтобы проверить работоспособность кода.
Также можно использовать Developer Tools (инструменты разработчика) веб-браузера, нажав на кнопку F12 на клавиатуре. В инструментах разработчика есть вкладка "Console" (консоль), где можно написать код на JavaScript и выполнить его, а также увидеть результат выполнения.
Error и стандартные ошибки
Какие бывают стандартные ошибки в JavaScript и как создавать собственные типы ошибок.
Время чтения: меньше 5 мин
Открыть/закрыть навигацию по статье
- Алекс Стегура ,
- Alex Baumgertner
Обновлено 31 июля 2022
Кратко
Скопировать ссылку "Кратко" Скопировано
Программа может работать правильно, только если код написан корректно и не содержит ошибок. JavaScript умеет обрабатывать некорректный код и сообщать об ошибке в коде. Существует семь встроенных видов ошибок, также можно создать свои собственные. Встроенные ошибки генерируются самим движком JavaScript при выполнении программы, а пользовательские — создаются с помощью конструктора Error . Оба типа ошибок можно ловить в конструкции try . . . catch .
Как понять
Скопировать ссылку "Как понять" Скопировано
Error
Скопировать ссылку "Error" Скопировано
Общий конструктор ошибок.
new Error('Общая ошибка. Проверьте код')new Error('Общая ошибка. Проверьте код')
Вызов конструктора возвращает объект ошибки со следующими свойствами:
- message представляет человекопонятное описание ошибки для встроенных типов ( Syntax Error , Type Error и так далее) и переданное в конструктор значение для общего типа Error .
- name — имя типа (класса) ошибки.
const commonError = new Error('Общая ошибка. Проверьте код') console.log(commonError.message)// 'Общая ошибка. Проверьте код' console.log(commonError.name)// 'Error'const commonError = new Error('Общая ошибка. Проверьте код') console.log(commonError.message) // 'Общая ошибка. Проверьте код' console.log(commonError.name) // 'Error'
Нестандартное свойство stack показывает, на какой строке кода возникла ошибка. Первая строка отформатирована как : , и за ней следует серия кадров стека (каждая строка начинается с «at»).
ReferenceError: FAIL is not defined at Constraint.execute (deltablue.js:525:2) at Constraint.recalculate (deltablue.js:424:21) at Planner.addPropagate (deltablue.js:701:6) at Constraint.satisfy (deltablue.js:184:15) at Planner.incrementalAdd (deltablue.js:591:21) at Constraint.addConstraint (deltablue.js:162:10) at Constraint.BinaryConstraint (deltablue.js:346:7) at Constraint.EqualityConstraint (deltablue.js:515:38) at chainTest (deltablue.js:807:6) at deltaBlue (deltablue.js:879:2)
Встроенные ошибки
Скопировать ссылку "Встроенные ошибки" Скопировано
SyntaxError
Скопировать ссылку "SyntaxError" Скопировано
Чаще всего встречаются опечатки — неправильные названия методов, лишние или отсутствующие точки с запятой или скобочки и так далее. Такой тип ошибок называется «синтаксическим», Syntax Error :
console.log(;)// SyntaxError: Unexpected token ';' console.log(()// SyntaxError: missing ) after argument listconsole.log(;) // SyntaxError: Unexpected token ';' console.log(() // SyntaxError: missing ) after argument list
ReferenceError
Скопировать ссылку "ReferenceError" Скопировано
Если попытаться обратиться к несуществующей переменной, произойдёт ошибка Reference Error :
console.log(name)// ReferenceError: name is not definedconsole.log(name) // ReferenceError: name is not defined
TypeError
Скопировать ссылку "TypeError" Скопировано
Если попытаться обратиться к несуществующему свойству, произойдёт ошибка Type Error :
console.log(null.length)// TypeError: Cannot read property 'length' of null undefined()// TypeError: undefined is not a functionconsole.log(null.length) // TypeError: Cannot read property 'length' of null undefined() // TypeError: undefined is not a function
RangeError
Скопировать ссылку "RangeError" Скопировано
Ошибка для значений, которые выходят за диапазон допустимого.
new Array(10000000000)// RangeError: Недопустимая длина массиваnew Array(10000000000) // RangeError: Недопустимая длина массива
URIError
Скопировать ссылку "URIError" Скопировано
Этот тип ошибок возникает при неправильном использовании обработки URI.
decodeURIComponent('%')// URIError: URI malformeddecodeURIComponent('%') // URIError: URI malformed
Валидным считается URI, формат которого соответствует спецификации RFC 3986:
URI = scheme:[//authority]path[?query][#fragment]
EvalError
Скопировать ссылку "EvalError" Скопировано
EvalError представляет ошибку, возникающую в глобальной функции eval ( ) .
eval( 'console.log(null.length)')eval( 'console.log(null.length)' )
Эта ошибка в настоящее время не используется и остаётся для совместимости с предыдущими версиями JavaScript.
InternalError (не стандарт)
Скопировать ссылку "InternalError (не стандарт)" Скопировано
Ошибка внутри движка JavaScript. Не является стандартом и почти не используется. Например:
"InternalError: инициализатор массива слишком большой".
Собственный класс ошибок
Скопировать ссылку "Собственный класс ошибок" Скопировано
Можно расширять базовый класс Error и создавать собственные типы ошибок.
class WrongDataTypeForSumError extends Error constructor(message) super(message) this.name = 'WrongDataTypeForSumError' >> const myCustomError = new WrongDataTypeForSumError('Невалидный тип данных для суммирования')class WrongDataTypeForSumError extends Error constructor(message) super(message) this.name = 'WrongDataTypeForSumError' > > const myCustomError = new WrongDataTypeForSumError('Невалидный тип данных для суммирования')
Сгенерируем ошибку WrongDataTypeForSum Error в случае, если хотя бы один из аргументов функции sum — не число.
function sum(a, b) if (typeof a !== 'number' || typeof b !== 'number') throw new WrongDataTypeForSumError('Невалидный тип данных для суммирования') > return a + b> console.log(sum('1', 2))// VM840:3 Uncaught WrongDataTypeForSumError: Невалидный тип данных для суммирования// at sum (:3:11)// at :9:13// WrongDataTypeForSumError @ VM830:3// sum @ VM840:3// (anonymous) @ VM840:9function sum(a, b) if (typeof a !== 'number' || typeof b !== 'number') throw new WrongDataTypeForSumError('Невалидный тип данных для суммирования') > return a + b > console.log(sum('1', 2)) // VM840:3 Uncaught WrongDataTypeForSumError: Невалидный тип данных для суммирования // at sum (:3:11) // at :9:13 // WrongDataTypeForSumError @ VM830:3 // sum @ VM840:3 // (anonymous) @ VM840:9
Функция будет выполняться только в том случае если оба аргумента будут числами, в противном случае функция будет возвращать ошибку WrongDataTypeForSum Error .
Собственные типы ошибок делают отладку более наглядной — например из имени WrongDataTypeForSum Error сразу понятно, что не так с кодом. Стандартная ошибка для таких случаев, Type Error — менее читаема.
JavaScript: Синтаксические ошибки
Если программа на JavaScript написана синтаксически некорректно, то интерпретатор выводит на экран соответствующее сообщение, а также указание на файл и строчку в нём, где, по его мнению, произошла ошибка. Синтаксическая ошибка возникает в том случае, когда код был записан с нарушением грамматических правил. В человеческих языках грамматика важна, но текст с ошибками чаще всего можно понять и прочитать. В программировании всё строго. Любое мельчайшее нарушение, и программа даже не запустится. Примером может быть забытая ; , неправильно расставленные скобки и другие детали.
Вот пример кода с синтаксической ошибкой:
console.log('Hodor'
Если запустить код выше, то мы увидим следующее сообщение: SyntaxError: missing ) after argument list , а также указание на строку и файл, где возникла эта ошибка. Подобные синтаксические ошибки в JavaScript относятся к разряду SyntaxError.
По техническим причинам, такой код, запущенный на https://code-basics.com не укажет на строку и файл. Проверить этот вывод можно на https://repl.it
С одной стороны, ошибки SyntaxError — самые простые, потому что они связаны исключительно с грамматическими правилами написания кода, а не с самим смыслом кода. Их легко исправить: нужно лишь найти нарушение в записи.
С другой стороны, интерпретатор не всегда может чётко указать на это нарушение. Поэтому бывает, что забытую скобку нужно поставить не туда, куда указывает сообщение об ошибке.
Задание
Это задание не связано с уроком напрямую. Но будет полезным потренироваться с выводом на экран.
Выведите на экран What Is Dead May Never Die
Упражнение не проходит проверку — что делать?
Если вы зашли в тупик, то самое время задать вопрос в «Обсуждениях». Как правильно задать вопрос:
- Обязательно приложите вывод тестов, без него практически невозможно понять что не так, даже если вы покажете свой код. Программисты плохо исполняют код в голове, но по полученной ошибке почти всегда понятно, куда смотреть.
В моей среде код работает, а здесь нет
Тесты устроены таким образом, что они проверяют решение разными способами и на разных данных. Часто решение работает с одними входными данными, но не работает с другими. Чтобы разобраться с этим моментом, изучите вкладку «Тесты» и внимательно посмотрите на вывод ошибок, в котором есть подсказки.
Мой код отличается от решения учителя
Это нормально , в программировании одну задачу можно выполнить множеством способов. Если ваш код прошел проверку, то он соответствует условиям задачи.
В редких случаях бывает, что решение подогнано под тесты, но это видно сразу.
Прочитал урок — ничего не понятно
Создавать обучающие материалы, понятные для всех без исключения, довольно сложно. Мы очень стараемся, но всегда есть что улучшать. Если вы встретили материал, который вам непонятен, опишите проблему в «Обсуждениях». Идеально, если вы сформулируете непонятные моменты в виде вопросов. Обычно нам нужно несколько дней для внесения правок.
Кстати, вы тоже можете участвовать в улучшении курсов: внизу есть ссылка на исходный код уроков, который можно править прямо из браузера.
Полезное
Определения
- Синтаксическая ошибка — нарушение грамматических правил языка программирования.
- SyntaxError (ошибка парсинга) — тип ошибок в JavaScript, возникающих при наличии синтаксических ошибок в коде.
