12. Наследование от ошибок Error

Несколько особняком, в  Node.JS, стоит наследование от встроенного объекта ошибки «Error». На этом примере

я сначала объясню, зачем оно может понадобиться, а потом как его делать правильно.
Здесь код состоит, по сути, из двух функций. Первая «function getPhrase(name)» это самая простейшая функция по интернационализации, которая только может быть. Она берет название фразы «name» и возвращает соответствующий перевод, если он есть. А если нет, то исключение. Вторая «function makePage(url)» получает «url», сейчас она умеет работать только с «index.html», поэтому, для остальных, она кидает ошибку, а если это «index.html» то возвращает такую вот форматированную строку — «return util.format(«%s, %s!», getPhrase(«Hello»), getPrase(«world»));». Давайте посмотрим, как работает такой код, запускаем

Screenshot_12_01

пока работает.
А теперь давайте добавим к этому коду правильную обработку ошибок. Например, если мы получили не корректный URL, несуществующий,

тогда «function makePage(url)» бросит ошибку, «Нет такой страницы». Если представить себе, что это веб сервер, то означает, что мы должны вывести юзеру сообщение «HTTP 404» Страница не найдена. Это один вариант ошибки, одна обработка. А посмотрим, что если фраза неизвестна. Если в каком то месте кода, мы вызвали получение фразы например вот так

Нет такой фразы, это уже совсем другая ошибка. И тогда нужно уже не 404 сделать, а статус 500 и написать уведомление системному администратору, что что-то тут не так, что словарь не полон и его срочно нужно поправить. Это программная ошибка, по этому обрабатывать ее нужно по другому.

К сожалению в текущем коде, даже если я добавлю «try/catch» к функции «makePage», все равно понять где какая ошибка, нельзя. И то и другое, это просто ошибки — класс «Error». С точки зрения объектно ориентированного программирования разумный способ решить эту проблему, это сделать свои объекты ошибки для разных случаев.

Это будет «PhraseError» и «HttpError», в ее конструкторе мы заодно укажем статус.

Что ж, объявим соответствующие классы при помощи «util.inherits»

Сразу же посмотрим на особенности работы над объектом ошибок. Какие нам свойства здесь важны. Первое, конечно же, «message». Для того, чтобы поставить «message» мне нужно сделать это вручную. Это есть особенность такая, работы с ошибками. То есть не вызов стандартного родителя суперкласса — вот так мы обычно конструктор супер класса вызываем

В данном случае ничего полезного он нам не сделает, необходимо поставить вручную

Следующие свойство, которое есть у всех встроенных ошибок, это «name». По свойству «name» мы можем точно понять, что за ошибка у нас есть. Оно у всех встроенных ошибках в прототипе у нас есть, по этому и сюда тоже мы запишем.

и

Наконец, последнее свойство, которое нам будет важно, это «stack». О нем мы поговорим чуть позже.

И так, давайте я сейчас запущу этот код. Как видим тут

«makePage(‘index’)» это неизвестный URL, по этому мы должны получить ошибку ‘HttpError’. Такую ошибку мы обрабатываем вот так

То есть просто выводим, без всякой паники. Запускаю

Screenshot_12_02

Увидели то, что должны были увидеть, все работает.

Теперь посмотрим другой вариант, а именно, если URL правильный, но ошибка произошла в программе, то есть видите, тут какая то не понятная фраза «Hell» вместо «Hello»

В данном случае, то есть если ошибка какая то другая, мы должны на нее отреагировать тоже иначе. Паника, кошмар, программная ошибка, срочно всех поднимаем, исправляем. Эта ветка кода будет действовать для всех программных ошибок, в том числе и для встроенных, а не только для «PhraseError». Запускаем

Screenshot_12_03

Вывело действительно «console.error», посмотрим на свойства —

  • Ошибка — «PhraseError», первое правильно
  • сообщение — «Нет такой фразы: Hell», тоже правильно
  • стек — «undefined»

Стек, это можно сказать самое важное. Свойство «stack» должно хранить информацию о том где, в каком файле произошла эта ошибка, что ей предшествовало.

Здесь, в конструкторах

я не произвел никаких действий, специальных которые правильно поставят стек. И в стандартах JavaScript их вообще не предусмотрено. Я просто создаю новый объект.

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

Эта команда получает текущий стек, то есть последовательность сложенных вызовов, которые привели к текущему месту кода и сохраняет его в «this», то есть в объекте ошибки. Давайте посмотрим на наш код

Запустим

Screenshot_12_04

Вот теперь, оно вывело стек, то есть где это все произошло. Но если посмотреть внимательно, здесь есть небольшой грешок. А именно, ошибка произошла, на самом деле, вот здесь

Нас интересует, то что произошло тут и как мы к этому дошли. То есть, нас интересует вот эта вот часть

Screenshot_12_05

до «getPhrase», а то что происходило внутри «new PhraseError» нас в принципе не интересует. То есть вот эта строчка стека

Screenshot_12_06

в данном случае лишняя. Чтобы убрать ее, в «captureStackTrace(this)» предусмотрен специальный параметр, второй, необязательный, это функция, до которой будет собираться «stackTrace». То есть если здесь я укажу текущий конструктор

то при этом, тот стек который внутри указанной функции, не будет показан в выводе, не будет собран

Screenshot_12_07

Я перезапустил и лишняя строка теперь вырезана.

И так мы получили унаследованные объекты ошибки с правильными свойствами «message», «name» и с возможностью вывести «stack».