Exception catching in javascript

Перехват ошибок, «try..catch»

Материал на этой странице устарел, поэтому скрыт из оглавления сайта.

Более новая информация по этой теме находится на странице https://learn.javascript.ru/try-catch.

Как бы мы хорошо ни программировали, в коде бывают ошибки. Или, как их иначе называют, «исключительные ситуации» (исключения).

Обычно скрипт при ошибке, как говорят, «падает», с выводом ошибки в консоль.

Но бывают случаи, когда нам хотелось бы как-то контролировать ситуацию, чтобы скрипт не просто «упал», а сделал что-то разумное.

Для этого в JavaScript есть замечательная конструкция try..catch .

Конструкция try…catch

Конструкция try..catch состоит из двух основных блоков: try , и затем catch :

  1. Выполняется код внутри блока try .
  2. Если в нём ошибок нет, то блок catch(err) игнорируется, то есть выполнение доходит до конца try и потом прыгает через catch .
  3. Если в нём возникнет ошибка, то выполнение 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 здесь не поможет. Такие ошибки называются синтаксическими, интерпретатор не может понять такой код.

Здесь же мы рассматриваем ошибки семантические, то есть происходящие в корректном коде, в процессе выполнения.

Ошибку, которая произойдёт в коде, запланированном «на будущее», например в setTimeout , try..catch не поймает:

На момент запуска функции, назначенной через 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 .

Выглядит этот расширенный синтаксис так:

Секция finally не обязательна, но если она есть, то она выполняется всегда:

Попробуйте запустить такой код?

Источник

Читайте также:  Python psycopg2 create database
Оцените статью