Try… catch VS if…else. Что, когда и почему? JavaScript: Исключения Пример использования конструкции TRY…CATCH для обработки ошибок

Данная статья расчитана для двух типов разработчиков:
- кто еще не знаком с исключениями
- кто пытается найти более разумное использование исключениям

В статье я расскажу о самых основах исключений, о том как можно обходиться без них, а так же о том, как я вижу правильным использование некоторых возможностей языка…

Эволюция языков программирования приводит иногда к кардинальным изменениям в мировоззрении разработчиков. В мире РНР такое случилось при появлении пятой ветки, которая принесла новую объектную модель, новые наборы встроенных функций и новые методы обработки ошибок…

Те, кто начинал знакомиться с PHP (тут и далее я буду подразумевать пятую версию), после другого процедурного языка программирования, так и не поняли что же такого в переходе от 4ки к 5ке и продолжают оформлять код привычными функциями, которые находятся на одном уровне по отношению друг к другу, а так же каждое действие проверяют на удачный код возврата. Но те кто знали об исключениях и классах…

Теория Исключение (Exception) - сигнал отправляемый программой в интерпретатор, о возникновении нештатной (исключительной) ситуации во время исполнения кода.

В PHP работа с исключениями основана на тех же принципах что и везде. Для генерации исключения используется оператор throw, которому передается объект исключения, принимающий в конструкторе два необязательных параметра: сообщение исключения и код исключения.

throw new Exception(\\\"This is exception message\\\", $exception_code);

Для того, что бы отловить исключение, используется конструкция try...catch. В блоке try выполняются операции, которые могут привести к исключительной ситуации, а блок catch позволяет принять решение что делать, если исключение было брошено.

try {
throw new Exception(\\\"Exception message\\\");
echo \\\"That code will never been executed\\\";
} catch (Exception $e) {
echo $e->getMessage(); //выведет \\\"Exception message\\\"
}

Как видно из примера, при выбрасывании исключения, остальной код в блоке try выполнен не будет, а управление будет передано в оператор catch, в котором мы указываем, как будет называться объект, в который будет передано выброшенное исключение (в нашем случае - $e). Внутри блока оператора catch, на основании данных из исключения мы можем применять какое-либо действие в зависимости от ситуации. Сейчас мы просто вывели сообщение, которое было передано исключением.

Объект Exception (который кстати можно унаследовать) имеет следующий набор финальных (final) методов:

final function getMessage(); // сообщение исключения
final function getCode(); // код исключения
final function getFile(); // файл из которого брошено исключение
final function getLine(); // строка бросания
final function getTrace(); // массив стека вызовов
final function getTraceAsString(); // массив стека вызовов отформатированый в строку
Практика Когда же удобно использовать исключения? Да всегда, когда функция или метод может прийти к ошибочной ситуации! На примере встроенной функции mysql_connect() и mysql_select_db(), если бы они бросала исключения при ошибке соединения и выборе базы данных соответственно, код выглядел бы так:
try {
mysql_connect($hostname, $username, $password);
mysql_select_db($dbname);
} catch (Exception $e) {
echo $e->getMessage(); //выведет либо сообщение об ошибке подключения, либо об ошибке выбора
}

И в зависимости от результата мы бы:

* удачно присоединились бы к СУБД и выбрали БД (код в сatch блоке не выполнился бы)
* при неудаче соединения с СУБД, вывели бы соответствующую ошибку и прикатили выполнение сеанса
* при ошибке выбора БД, оповестили бы пользователя об этой неприятности

Теперь самое время спросить: \\\«А зачем нам такая сложная штука, если можно использовать оператор if?\\\».

Первый же ответ: \\\«Потому что так код более читабельный, а обработка ошибок выведена в отдельный блок\\\». Если бы мы использовали оператор if, то код выглядел бы следующим образом:

$connId = mysql_connect($hostname, $username, $password);
if (false == $connId) {
echo \\\"Ошибка подключения к СУБД\\\";
}

$flag = mysql_select_db($dbname);
if (false == $flag) {
echo \\\"Невозможно выбрать базу данных.\\\";
}

Согласитесь, что в предыдущем варианте код выглядит более понятным. Но это еще не все. Теперь обработкой этих ошибок заниматься непосредственно разработчику не обязательно - достаточно выбросить исключение, а его обработкой можно заняться на более высоком уровне. Так же исключения можно передавать цепочкой (chain) наверх:

class MyException extends Exception {}

Try {
try {
//...
throw new Exception(\\\"inner\\\");
//...
} catch (Exception $e) {
throw new MyException(\\\"outer\\\");
}
} catch (MyException $e) {
echo $e->getMessage(); //выведет \\\"outer\\\"
}

Производительность Пытаясь достичь истины, провел несколько экспериментов с различными типами функций.
Первый тип возвращал статус true и проверялся операторами if...else
Второй тип возвращал статус false и проверялся операторами if...else
Третий тип просто выполнял действия и ничего не возвращал. Проверялся блоком try...catch
Четвертый тип всегда бросал исключение и проверялся в try...catch

Результаты:

True: 0.72382092475891
False: 0.85190796852112
No exception: 0.72565317153931
Exception: 14.176206827164

Как и ожидалось - бросание исключений довольно накладная операция, а вот оба варианта удачного выполнения прошли тесты вровень.
Тесты проводились на примитивных функциях сложения, но в 1кк итераций

ВыводыКогда же использовать исключения? Всегда, когда подразумевается ошибка или нестандартное поведение программы, а так же когда принятие решения об обработке результата необходимо переложить на более высокий уровень.
А что же делать с оператором if и булевыми статусами отработки функций? Оставить их. Но толко там где они действительно необходимы. Тоесть там, где логический оператор подразумевает использование результата в вычислениях, а не контроле потока выполнения. Тоесть все удачные завершения функций теперь не нужно оповещать оператором return true, если это логическое значение не пригодиться для дальнейших вычислений. И в то же время все статусы завершения функций с ошибками изменить из формата return false в формат throw new Exception()
Что нужно помнить? Использование исключений предполагает что весь код выполняется со статусом true (без ошибок), но если ошибка произошла, то для ее обработки всегда найдется место в блоке catch.

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

Alert(x); // Ошибка. Используется неопределённая переменная

Чтобы, при возникновении исключительной ситуации, сценарий не просто прекратил свою работу, а сделал что-то полезное, можно использовать специальные инструкции try/catch/finally . Данные инструкции позволяют хоть как-то контролировать ситуацию при возникновении непредвиденных ошибок.

Инструкция try/catch

C помощью инструкции try/catch исключение можно перехватить и обработать. Синтаксис инструкции try/catch:

Try { // Исполняемый код } catch (e) { // При возникновении ошибки, делаем что-то }

В блоке try располагают код, в котором может возникнуть ошибка во время выполнения программы, а блок catch позволяет принять решение, что делать при возникновении ошибки. За ключевым словом catch следует идентификатор в круглых скобках. Этот идентификатор похож на параметр функции. При возникновении исключительной ситуации в блоке try , этому параметру в качестве аргумента неявно передаётся объект Error , содержащий сведения об ошибке. Например, объект Error имеет свойство name , содержащее сведения о типе ошибки и свойство message содержащее описание ошибки. Имя параметра в круглых скобках может быть любым допустимым идентификатором:

Try { alert(x); alert(2 + 2); // Не будет выполнено } catch (e) { alert(e.name + ": " + e.message); }

Как видно из примера, при возникновении исключения выполнение кода в блоке try прекращается и управление передаётся блоку catch . Если в блоке try ошибка не возникает, то блок catch просто игнорируется:

Try { alert("Код в блоке try"); // Код без ошибок } catch(e) { alert("Блок catch игнорируется"); } alert("Продолжается выполнение кода после инструкции try/catch...");

Без инструкции try/catch сообщения об ошибках автоматически выводятся в консоль браузера.

Блок finally

К инструкции try/catch можно добавить ещё один блок – finally , который будет выполняться всегда. Это означает, что он выполняется и при возникновении исключения, сразу после блока catch , и без исключения, сразу после того, как блок try завершит выполнение:

Try { alert("Код в блоке try"); // Код без ошибок } catch(e) { alert("Блок catch игнорируется"); } finally { alert("Код в блоке finally"); } try { alert(x); // Ошибка } catch(e) { alert("Код в блоке catch"); } finally { alert("Код в блоке finally"); }

Если исключение никак не планируется обрабатывать то, при наличии блока finally блок catch можно опустить:

Try { alert(x); } finally { alert("Код в блоке finally"); }

Блоки try , catch и finally начинаются и заканчиваются фигурными скобками. Это обязательная часть их синтаксиса, и она не может быть опущена, даже если внутри блока содержится только одна инструкция.

Инструкция throw

Инструкция throw позволяет явно сгенерировать исключение. Она имеет следующий синтаксис:

Throw выражение;

Результатом выражения может быть значение любого типа. Например, это может быть число, представляющее код ошибки, или строка, содержащая текст сообщения об ошибке. Обычно, в качестве выражения создают стандартный объект типа Error . Значение выражения неявно передаётся в качестве аргумента блоку catch:

Try { var x = -1; if (x < 0) throw new Error("x не может быть меньше нуля!"); } catch(e) { alert(e); } try { throw "Hello!"; } catch(e) { alert(e); }

Если инструкция throw используется без инструкции try/catch то, сгенерированное исключение выводится в консоль браузера.

На этом уроке мы познакомимся с оператором обработки исключений try...catch , который предназначен для перехвата ошибок в некотором блоке кода и их обработки.

Применение оператора try...catch

Оператор обработки исключений try...catch обычно применяется в следующих ситуациях:

  • Для некоторого участка кода, в котором может возникнуть ошибка;
  • Для валидации (проверки) данных перед отправкой на сервер;
  • Для использования в сложных случаях при написании кроссбраузерных приложений.

Теперь давайте рассмотрим, что же произойдет в сценарии на JavaScript при возникновении ошибки и для чего нужен оператор try...catch .

Если в сценарии на языке JavaScript появится ошибка, то дальнейшее выполнение программы будет прекращено. Т.е. инструкции (операторы), которые идут после ошибки выполняться не будут. Но если нам необходимо выполнение программы не прерывать, а переходить к следующей инструкции после той, где может возникнуть ошибка, то эту инструкцию необходимо заключить в оператор " try...catch ".

Синтаксис оператора try...catch

Принцип работы с оператором try...catch заключается в следующем:

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

Принцип работы оператора try...catch

Оператор обработки исключений работает следующим образом:

Сначала он пытается выполнить все инструкции (операторы), указанные в блоке try . Если внутри блока try ошибки не возникает, то блок catch просто игнорируется, т.е. он не выполняется. В том случае если внутри блока try возникает ошибка, то оставшиеся операторы в этом блоке будут проигнорированы (т.е. они выполняться не будут) и сразу же начнут выполняться операторы внутри блока catch . Самое главное то, что инструкции, идущие после оператора try...catch , продолжат выполняться и работа программы не остановится.

Try { document.write("Первая строка"); documant.write("Вторая строка"); document.write("Третья строка"); } catch (e) { alert("Ошибка:" + e.message); }

В результате выполнения кода в 4 строчке произойдет ошибка, т.к. оператор documant браузером будет не определён. В этом случае 5 строчка браузером выполнена не будет, т.к. начнут выполняться операторы в блоке catch . В качестве параметра блока catch определим переменную e , в которой будет хранить информацию об ошибке. Данную переменную мы будем использовать в операторе alert для вывода сообщения об ошибке.

Блок finally

У оператора обработки исключений есть ещё один блок, который называется finally . Данный блок является не обязательным, и его можно использовать только при необходимости. Он не имеет параметров и в отличие от блока catch выполняется всегда, вне зависимости от того возникла ошибка или нет.

Но так как инструкции, идущие после оператора try...catch тоже выполняются всегда, то возникает вопрос: "Зачем нужен блок finally?"

На самом деле, инструкции, идущие после try...catch , выполняются не всегда. Это может произойти только в том случае, если произойдет ошибка в блоке catch . Когда это произойдёт, программа приостановит своё выполнение и операторы, идущие после конструкции try...catch выполняться не будут. Единственный блок инструкций (операторов), который будет выполняться – это блок finally .

Try { document.write("Первая строка
"); documant.write("Вторая строка
"); document.write("Третья строка
"); } catch (e) { alrt("Ошибка:" + e.message); } finally { document.write("Завершающие действия"); } alert("Операторы после try...catch");

В результате выполнения кода оператор alert , идущий после конструкции try...catch так и не будет выполнен.

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

Ошибки бывают в основном 2-х типов — это синтаксические и логические. К синтаксическим можно отнести ошибки в имени переменных, функций, ошибки в синтаксисе кода. В принципе такие ошибки легко отловить через консоль браузера.

А вот логические ошибки с ними все не так просто потому, что они приводят к неправильному выполнению кода программы. Поэтому для их устранения потребуется отладка программы, чтобы понять что собственно происходит на каждом шаге скрипта. Мы же с вами здесь рассмотрим в основном локализацию синтаксических ошибок с помощью конструкции try…catch.



Конструкция перехвата ошибок try…catch

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

Try { // код... } catch (err) { // обработка ошибки }

Работает эта конструкция таким образом:

  • Выполняется код внутри блока try, так называемой ловушки.
  • Если в нём не встречаются ошибки, то блок catch(err) игнорируется.
  • А вот, если в нём возникнет ошибка, то выполнение try будет прервано на ошибке, и управление передается в начало блока catch(err). При этом переменная err (можно выбрать любое другое название) будет содержать объект ошибки с подробнейшей информацией о произошедшей ошибке.
  • Поэтому при ошибке в try скрипт не останавливается, и даже более того мы имеем возможность обработать ошибку внутри блока catch.

    Рассмотрим это на примерах.

    • Пример без ошибок: при запуске сработают alert (1) и (2): try { alert("Блок try"); // (1)

    Публикации по теме