Try… catch VS if…else. Что, когда и почему? JavaScript: Исключения Пример использования конструкции TRY…CATCH для обработки ошибок
Данная статья расчитана для двух типов разработчиков:
- кто еще не знаком с исключениями
- кто пытается найти более разумное использование исключениям
В статье я расскажу о самых основах исключений, о том как можно обходиться без них, а так же о том, как я вижу правильным использование некоторых возможностей языка…
Эволюция языков программирования приводит иногда к кардинальным изменениям в мировоззрении разработчиков. В мире РНР такое случилось при появлении пятой ветки, которая принесла новую объектную модель, новые наборы встроенных функций и новые методы обработки ошибок…
Те, кто начинал знакомиться с PHP (тут и далее я буду подразумевать пятую версию), после другого процедурного языка программирования, так и не поняли что же такого в переходе от 4ки к 5ке и продолжают оформлять код привычными функциями, которые находятся на одном уровне по отношению друг к другу, а так же каждое действие проверяют на удачный код возврата. Но те кто знали об исключениях и классах…
В PHP работа с исключениями основана на тех же принципах что и везде. Для генерации исключения используется оператор throw, которому передается объект исключения, принимающий в конструкторе два необязательных параметра: сообщение исключения и код исключения.
throw new Exception(\\\"This is exception message\\\", $exception_code);Для того, что бы отловить исключение, используется конструкция try...catch. В блоке try выполняются операции, которые могут привести к исключительной ситуации, а блок catch позволяет принять решение что делать, если исключение было брошено.
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 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, то код выглядел бы следующим образом:
if (false == $connId) {
echo \\\"Ошибка подключения к СУБД\\\";
}
$flag = mysql_select_db($dbname);
if (false == $flag) {
echo \\\"Невозможно выбрать базу данных.\\\";
}
Согласитесь, что в предыдущем варианте код выглядит более понятным. Но это еще не все. Теперь обработкой этих ошибок заниматься непосредственно разработчику не обязательно - достаточно выбросить исключение, а его обработкой можно заняться на более высоком уровне. Так же исключения можно передавать цепочкой (chain) наверх:
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
Результаты:
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/catchC помощью инструкции 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.
Рассмотрим это на примерах.
- Пример без ошибок: при запуске сработают alert (1) и (2): try { alert("Блок try"); // (1)