Как сделать ошибку в js

Добавил пользователь Евгений Кузнецов
Обновлено: 10.09.2024

По какой-то причине похоже, что делегирование конструктора не работает в следующем фрагменте:

Запуск этого дает The message is: '' . Любые идеи о том, почему, или если есть лучший способ создать новый подкласс Error ? Есть ли проблема с apply ing для встроенного конструктора Error , о котором я не знаю?

Обновите свой код, чтобы назначить ваш прототип Error.prototype, а instanceof и ваши утверждения работают.

Однако я бы просто выбросил ваш собственный объект и просто проверил свойство name.

Изменить на основе комментариев

После просмотра комментариев и попыток вспомнить, почему я бы назначил прототип Error.prototype вместо new Error() , как это сделал Николас Закас в своей статье , я создал jsFiddle с помощью кода ниже:

Возможно, вы могли бы совершить какую-либо обман, чтобы перечислить все неперечислимые свойства tmp Error, чтобы установить их, а не явно устанавливать только stack и message , но обман не поддерживается в ie B T 26 июл. '13 в 21:12

Если кому-то интересно узнать, как создать пользовательскую ошибку и получить трассировку стека:

В ES2015 вы можете использовать class для этого:

Это не изменяет глобальный прототип Error , позволяет настраивать message , name и другие атрибуты и правильно захватывать стек. Это также довольно читаемо.

Конечно, вам может понадобиться использовать такой инструмент, как babel , если ваш код будет работать в старых браузерах.

Этот раздел стандарта может объяснить, почему вызов Error.apply не инициализирует объект:

15.11.1 Конструктор ошибок, вызываемый как функция

Когда Ошибка вызывается как функция, а не как конструктор, она создает и инициализирует новый объект Error. Таким образом, вызов функции Error (. ) эквивалентно выражению создания объекта new Error (. ) с те же аргументы.

В этом случае функция Error , вероятно, определяет, что она не вызывается как конструктор, поэтому возвращает новый экземпляр Error, а не инициализирует объект this .

Тестирование с помощью следующего кода, похоже, показывает, что это на самом деле происходит:

При этом запускается следующий вывод:

У меня была аналогичная проблема. Моя ошибка должна быть instanceof как Error , так и NotImplemented , а также необходимо создать согласованную обратную трассировку в консоли.

Результат работы с node.js:

Ошибка пропускает все 3 из моих критериев, и хотя свойство stack нестандартно, оно поддерживается в большинстве новых браузеров, которое приемлемый в моем случае.

Соглашаясь с Joyent, вы не должны испортить свойство стека (что я вижу во многих ответах, приведенных здесь), поскольку это будет иметь негативное влияние на производительность. Вот что они говорят:

stack: вообще, не путайте с этим. Даже не увеличивайте его. V8 только вычисляет его, если кто-то действительно читает свойство, что значительно повышает производительность для ручных ошибок. Если вы читаете свойство только для его увеличения, вы в конечном итоге оплачиваете стоимость, даже если вашему абоненту не нужен стек.

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

В JavaScript обработка ошибок работает через механизм исключений. Одни функции их возбуждают, другие обрабатывают через try..catch. Так было в синхронном коде. В асинхронном стандартный механизм уже не работает.

Подумайте над тем как отработает код ниже:

Из вывода видно, что колбек вызвался в своем стеке вызовов, начавшимся внутри функции readFile() . Фактически это означает, что использовать try/catch в асинхронном коде с колбеками — бесполезно, эта конструкция здесь просто неприменима.

Что выведет на экран код ниже?

Правильный ответ: finished!. Это кажется странным, учитывая что ошибка возникла внутри функции readFile() , а не в колбеке. Это происходит потому, что содержимое функции readFile() не принадлежит текущему стеку вызовов.

Асинхронные функции всегда имеют дело с внешней средой (операционной системой). Это значит, что любая асинхронная функция потенциально может завершиться с ошибкой. Причём не важно возвращает ли она какие-то данные или нет, ошибка может возникнуть всегда. Именно по этой причине колбеки всех асинхронных функций первым параметром принимают ошибку err и, соответственно, проверять её наличие придётся руками. Если пришёл null , то ошибки нет, если не null — есть. Это очень важное соглашение, которого придерживаются не только разработчики стандартной библиотеки, но и все разработчики сторонних решений.

В цепочке вызовов придётся делать проверку на каждом уровне:

Тот же самый код, помещённый внутрь функции, выглядит немного по-другому. Как только происходит ошибка, мы вызываем основной колбек и отдаём туда ошибку. Если ошибка не возникла, то мы всё равно вызываем исходный колбек и передаём туда null . Вызывать его обязательно, иначе внешний код не дождётся окончания операции. Следующие вызовы больше не выполняются:

Последний вызов можно сократить. Если в самом конце не было ошибки, то вызов cb(error3) отработает так же, как и cb(null) , а значит, весь код последнего колбека можно свести к вызову cb(error3) :

JavaScript

Nuances of Programming

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

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

Исключения лучше, чем возврат кода ошибки

Исключения лучше потому, что они дают нам знать, что ошибка существует, и нам нужно её обработать.

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

Коды ошибок не такие явные, и их легко пропустить. Исключения намного понятнее, так как не нужно проверять весь код, возвращённый при ошибке.

Например, если мы возвращаем коды ошибок в функциях, получим подобный код:

Нам придётся возвращать все коды ошибок в методе setNumFruit . К тому же прежде, чем сделать что-то после определения класса, нужно будет проверить все коды ошибок.

Вместо этого используем исключения:

Мы устранили необходимость проверять все коды ошибок, обернув код, который мы хотим запустить, в блок try . Теперь ошибку просто отловить. И это особенно важно, когда код становится всё более сложным.

Пишите блок Try-Catch-Finally

Стоит оборачивать try в коде, генерирующем исключения, которые мы хотим отловить. Он создаёт собственную область видимости для переменных в области блока, поэтому на объявленное с помощью let или const можно ссылаться только в блоке try .

На переменные, объявленные с var , можно ссылаться вне блока — мы не получим ошибку. Такой код выдаст 1 :

А этот код выдаст Uncaught ReferenceError: x is not defined :

Не игнорируйте пойманные ошибки

Нужно сообщать об ошибках, когда они обнаруживаются, нельзя просто отловить их и проигнорировать — ведь мы не хотим заметать проблемы под ковёр.

Примеры выше, например console.error , вызовут следующее:

Не игнорируйте отклонённые промисы

Отклонённые промисы нужно обрабатывать, как и любые другие исключения. Их можно обработать с помощью обратного вызова, который передаётся в метод catch , или с помощью блока try. catch для функций async — они одинаковые.

Например, об ошибке можно сообщить, написав следующее:

Или для функций async напишем:

Предоставление контекста с помощью исключений

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

Можно получить трассировку стека с исключениями в JavaScript, но она может не предоставлять всю необходимую информацию.

Заключение

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

При бросании исключений мы должны предоставить достаточно детализированную информацию для точного определения проблемы.

Не стоит просто игнорировать ошибки после их обнаружения, нужно, по крайней мере, сообщать о них, чтобы их можно было просмотреть.

Наконец, отклонённые ошибки промисов нужно обрабатывать так же, как и остальные исключения.

Обработка ошибок - это боль. Вы можете довольно далеко продвинуться без правильной обработки ошибок, но чем больше приложение, тем с большими проблемами вы столкнетесь. Чтобы действительно вывести вашу разработку API на новый уровень, вам следует взяться за решение этой задачи. Обработка ошибок - это обширная тема, и ее можно выполнять разными способами, в зависимости от приложения, технологий и многого другого. Это одна из тех вещей, которые легко понять, но трудно понять полностью.

Что мы будем делать

В этой статье мы собираемся объяснить удобный для новичков способ обработки ошибок в API Node.js + Express.js с помощью TypeScript. Мы собираемся объяснить, что такое ошибка, различные типы ошибок, которые могут возникнуть, и как их обрабатывать в нашем приложении. Вот некоторые вещи, которыми мы займемся в следующих главах:

Предусловие

Предупреждение! В этой статье предполагается, что вы уже кое-что знаете. Несмотря на то, что это удобно для новичков, вот что вам следует знать, чтобы извлечь из этой статьи максимальную пользу:

  1. рабочее знание Node.js
  2. рабочее знание Express.js (маршруты, промежуточное ПО и т. д.)
  3. основы TypeScript (и классы!)
  4. основы работы API и его написания с использованием Express.js

Хорошо. Мы можем начинать.

Что такое обработка ошибок и зачем она вам нужна?

Обработка ошибок (или обработка исключений) - это процесс реагирования на возникновение ошибок (аномальное/нежелательное поведение) во время выполнения программы.

Зачем нужна обработка ошибок?

Потому что мы хотим сделать исправление ошибок менее болезненным. Это также помогает нам писать более чистый код, поскольку весь код обработки ошибок централизован, вместо того, чтобы обрабатывать ошибки там, где мы думаем, что они могут возникнуть. В конце концов - код более организован, вы меньше повторяетесь, и это сокращает время разработки и обслуживания.

Типы ошибок

Есть два основных типа ошибок, которые нам необходимо различать и соответственно обрабатывать.

Операционные ошибки

  1. Запрос API не выполняется по какой-либо причине (например, сервер не работает или превышен лимит скорости)
  2. Невозможно установить соединение с базой данных
  3. Пользователь отправляет неверные входные данные
  4. Системе не хватает памяти

Ошибки программиста

  1. Попытка прочитать свойство объекта, которое не определено
  2. Передача некорректных параметров в функции
  3. не улавливая отвергнутого обещания

Что такое ошибка узла?

В Node.js есть встроенный объект Error который мы будем использовать в качестве основы для выдачи ошибок. При вызове он содержит набор информации, которая сообщает нам, где произошла ошибка, тип ошибки и в чем проблема. В документации Node.js есть более подробное объяснение.

Мы можем создать такую ошибку:

Name и message не требуют пояснений, а stack содержит name , message и строку, описывающую точку в коде, в которой был создан Error . Этот стек на самом деле представляет собой серию стековых фреймов (подробнее о нем можно узнать здесь). Каждый фрейм описывает сайт вызова в коде, который приводит к сгенерированной ошибке. Мы можем вызвать стек console.log() ,

и посмотрим, что он может нам сказать. Вот пример ошибки, которую мы получаем при передаче строки в качестве аргумента функции JSON.parse() (которая завершится ошибкой, поскольку JSON.parse() принимает только данные JSON в строковом формате):


Написание собственных классов ошибок.

Пользовательские классы ошибок

Как я упоминал ранее, мы можем использовать встроенный объект Error , поскольку он дает нам ценную информацию.

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

Для этого мы можем написать класс, который расширит класс Error , добавив в него немного больше данных.

Мы будем использовать этот класс BaseError в качестве основы для всех наших пользовательских ошибок.

Теперь мы можем использовать класс BaseError , чтобы расширить его и сформировать все наши собственные ошибки. Это зависит от потребностей нашего приложения. Например, если мы собираемся иметь конечные точки аутентификации в нашем API, мы можем расширить класс BaseError и создать класс AuthenticationError :

Он будет использовать тот же конструктор, что и наш BaseError , но как только мы используем его в нашем коде, он значительно упростит чтение и отладку кода.

Теперь, когда мы знаем, как расширить объект Error , мы можем сделать еще один шаг.

Поскольку мы, вероятно, собираемся использовать ту же логику не только для продуктов (например, для пользователей, карт, местоположений), давайте сделаем эту ошибку повторно используемой.

А вот как это выглядит при сбросе:


Теперь мы можем создавать различные ошибки в соответствии с нашими потребностями. Вот некоторые из наиболее распространенных примеров API:

  1. ValidationError (ошибки, которые можно использовать при обработке входящих пользовательских данных)
  2. DatabaseError (ошибки, которые вы можете использовать, чтобы сообщить пользователю, что существует проблема с взаимодействием с базой данных)
  3. AuthenticationError (ошибка, которую можно использовать, чтобы сообщить пользователю об ошибке аутентификации)

Идем на шаг дальше

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

Например, вы можете использовать коды ошибок в AuthenticationError чтобы сообщить потребителю тип ошибки аутентификации. A01 может означать, что пользователь не проверен, а A02 может означать, что срок действия ссылки для сброса пароля истек.

Подумайте о потребностях вашего приложения и постарайтесь сделать его как можно проще.

Создание и обнаружение ошибок в контроллерах

Теперь давайте посмотрим на образец контроллера (функцию маршрута) в Express.js.

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

Это успешно остановит выполнение этой функции и передаст ошибку следующей функции промежуточного программного обеспечения. Так вот оно?

Не совсем. Нам все еще нужно обрабатывать ошибки, которые мы не обрабатываем с помощью наших пользовательских ошибок.

Необработанные ошибки

Например, предположим, что вы пишете фрагмент кода, который проходит все проверки синтаксиса, но выдает ошибку во время выполнения. Эти ошибки могут случиться, и они будут. Как мы с ними справляемся?

Допустим, вы хотите использовать функцию JSON.parse() . Эта функция принимает данные JSON в виде строки, но вы даете ей случайную строку. Если передать этой функции, основанной на обещаниях, строку, она выдаст ошибку! Если не обработать, это вызовет ошибку UnhandledPromiseRejectionWarning .


Что ж, просто оберните свой код в блок try/catch и передайте любые ошибки по строке промежуточного программного обеспечения, используя next() (опять же, я скоро объясню это)!

И это действительно сработает. Это неплохая практика, поскольку все ошибки, возникающие из-за кода, основанного на обещаниях, будут обнаружены внутри блока .catch() . Однако у этого есть обратная сторона, а именно тот факт, что ваши файлы контроллера будут заполнены повторяющимися блоками try/catch, и мы не хотим повторяться. К счастью, у нас есть еще один козырь в рукаве.

Оболочка handleAsync

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

Вот как это выглядит:

На первый взгляд это может показаться сложным, но это всего лишь функция промежуточного программного обеспечения, которая действует как блок try /catch с next(err) внутри catch() . Теперь мы можем просто обернуть его вокруг наших контроллеров, и все!

Теперь, если возникнет такая же ошибка, мы не получим UnhandledPromiseRejectionWarning , вместо этого наш код обработки ошибок успешно ответит и зарегистрирует ошибку (разумеется, после того, как мы закончим ее писать. Вот как это будет выглядеть):



Как мне обрабатывать ошибки?

Хорошо, мы научились формировать ошибки. Что теперь?

Экспресс промежуточное ПО

Экспресс-приложение, по сути, представляет собой серию вызовов функций промежуточного программного обеспечения. Функция промежуточного программного обеспечения имеет доступ к объекту request , объекту response и функции next промежуточного программного обеспечения.

Экспресс с маршрутизацией каждого входящего запроса через эти промежуточные программы, начиная с первого по цепочке, до тех пор, пока ответ не будет отправлен клиенту. Каждая функция промежуточного программного обеспечения может либо передать запрос следующему промежуточному программному обеспечению с помощью функции next(), либо ответить клиенту и разрешить запрос.

Выявление ошибок в Express

Вот пример того, как его написать:

Что делать с ошибками

Теперь, когда мы знаем, как обнаруживать ошибки, мы должны что-то с ними делать. В API обычно нужно сделать две вещи: ответить клиенту и записать ошибку.

errorReponse промежуточное ПО (ответ клиенту)

А теперь мы собираемся написать промежуточное программное обеспечение, которое обрабатывает часть сбоя.

Давайте рассмотрим функцию. Сначала мы создаем логическое значение customError . Мы проверяем свойство error.constructor.name , которое сообщает нам, с какой ошибкой мы имеем дело. Если error.constructor.name это NodeError (или какая-то другая ошибка, которую мы не создавали лично), мы устанавливаем логическое значение false, в противном случае мы устанавливаем true. Таким образом, мы можем по-разному обрабатывать известные и неизвестные ошибки.

Поскольку свойство statusCode доступно только в наших настраиваемых объектах ошибок, мы можем просто вернуть 500, если оно недоступно (то есть это необработанная ошибка).

В конце концов, мы используем функцию next() для передачи ошибки следующему промежуточному программному обеспечению.

errorLog промежуточное ПО (регистрация ошибки)

Эта функция следует той же логике, что и предыдущая, с небольшой разницей. Поскольку это ведение журнала предназначено для разработчиков API, мы также регистрируем стек.

Читайте также: