Throw new error msg

var err1 = Error('message'); var err2 = new Error('message'); What's the difference? Looking at them in the chrome console, they look identical. Same properties on the object and the same __proto__
var err1 = Error('message');
var err2 = new Error('message');

What’s the difference? Looking at them in the chrome console, they look identical. Same properties on the object and the same __proto__ chain. Almost seems like Error acts like a factory.

Which one is correct and why?

asked Nov 8, 2012 at 17:42

Ilia Choly's user avatar

Ilia CholyIlia Choly

17.8k14 gold badges92 silver badges155 bronze badges

2

Both are fine; this is explicitly stated in the specification:

… Thus the function call Error(…) is equivalent to the object creation expression new Error(…) with the same arguments.

Prasanna Venkatesh's user avatar

answered Nov 8, 2012 at 17:43

pimvdb's user avatar

3

Error does act like a factory, like some other native constructors: Array, Object, etc. all check something like if (!(this instanceof Array)) { return new Array(arguments); }. (But note that String(x) and new String(x) are very different, and likewise for Number and Boolean.)

That said, in case of an error, it’s not even required to throw an Error object… throw 'Bad things happened'; will work, too
You can even throw an object literal for debugging:

throw {message:"You've been a naughty boy",
       context: this,
       args: arguments,
       more:'More custom info here'};

wchargin's user avatar

wchargin

15.4k12 gold badges68 silver badges108 bronze badges

answered Nov 8, 2012 at 17:46

Elias Van Ootegem's user avatar

Elias Van OotegemElias Van Ootegem

73.3k9 gold badges109 silver badges147 bronze badges

7

The throw statement allows you to create an exception or a custom error. The exception can be like a Javascript string, a number, a boolean, or an object. So, If you use this statement together with the try…catch statement. It allows you to control the flow of the program and generate accurate error messages. For example: throw “too small” (throw a text), throw 500 (throw a number), etc.

First of all, throw Error() and throw new Error() are functionally equivalent.

Syntax:

throw Error("Enter your error message here");

Error object: An object containing the error information is generated and passed to catch as an argument. Error object has three properties: name, message, and stack.

Error() constructor is used to create a new error object.

Example: For better understanding, we will take an example:

Javascript

try {

    throw Error("msg");

} catch (e) {

    console.log("for Error()");

    console.log(e);

}

Output:

The output of the above code(Error)

In the above code, I added a try…catch statement with having throw Error(). This code will do that the  ‘msg’ error is thrown in the try-block which will be get executed by catch statements.

new Error object: It captures several properties of the place where the error happened.  It exposes an error event with two parameters name & message. It also terminates further execution.

Example: For better understanding, we will take an example:

Javascript

try {

    throw new Error("msg");

} catch (e) {

    console.log("for new Error()");

    console.log(e);

}

Output :

The output of the above code(new Error)

In the above code, I added a try…catch statement with having throw new Error(). This code will do that the  ‘msg’ error is thrown in the try-block which will be get executed by catch statements.  

As you can see in the images of both of the consoles, throw an error(‘msg’) and throw a new error(‘msg’) give the same results. 

Note: Error() and new Error() returns an object as a result.

Difference between throw Error() and throw new Error():

throw Error()

throw new Error()

 Error(…) is less reliable and consistent. new Error(…) is more reliable and consistent.
the Error object contains a stack trace and other useful debugging information which is lost when you use a string literal.  Creating objects using ES6 classes requires the use of new and extending Error via a class is the only way to preserve stack traces. 
We can create an exception or a custom error using throw Error() The new Error() method is an inbuilt application programming interface of the Node module
throw Error() is like a Javascript string, a number, a boolean, or an object.  It returns specific errors as defined in the message value which is passed as an argument.
We can control the flow of the program and generate accurate error messages using throw Error() It Creates a new Error object and sets the error.message property to the provided text message.
The throw statement throws an exception.

Its syntax is -:

new Error(message)

var err1 = Error('message');
var err2 = new Error('message');

What’s the difference? Looking at them in the chrome console, they look identical. Same properties on the object and the same __proto__ chain. Almost seems like Error acts like a factory.

Which one is correct and why?

asked Nov 8, 2012 at 17:42

Ilia Choly's user avatar

Ilia CholyIlia Choly

17.8k14 gold badges92 silver badges155 bronze badges

2

Both are fine; this is explicitly stated in the specification:

… Thus the function call Error(…) is equivalent to the object creation expression new Error(…) with the same arguments.

Prasanna Venkatesh's user avatar

answered Nov 8, 2012 at 17:43

pimvdb's user avatar

3

Error does act like a factory, like some other native constructors: Array, Object, etc. all check something like if (!(this instanceof Array)) { return new Array(arguments); }. (But note that String(x) and new String(x) are very different, and likewise for Number and Boolean.)

That said, in case of an error, it’s not even required to throw an Error object… throw 'Bad things happened'; will work, too
You can even throw an object literal for debugging:

throw {message:"You've been a naughty boy",
       context: this,
       args: arguments,
       more:'More custom info here'};

wchargin's user avatar

wchargin

15.4k12 gold badges68 silver badges108 bronze badges

answered Nov 8, 2012 at 17:46

Elias Van Ootegem's user avatar

Elias Van OotegemElias Van Ootegem

73.3k9 gold badges109 silver badges147 bronze badges

7

Конструктор Error создаёт объект ошибки. Экземпляры объекта Error выбрасываются при возникновении ошибок во время выполнения. Объект Error также может использоваться в качестве базового для пользовательских исключений. Смотрите ниже стандартные встроенные типы ошибок.

Синтаксис

new Error([message[, fileName[, lineNumber]]])

Параметры

message Необязательный

Человеко-читаемое описание ошибки.

fileName
Non-standard
Необязательный

Значение свойства fileName созданного объекта Error. Значением по умолчанию является имя файла, содержащего код, вызвавший конструктор Error().

lineNumber
Non-standard
Необязательный

Значение свойства lineNumber созданного объекта Error. Значением по умолчанию является номер строки, содержащей вызов конструктора Error().

Описание

Во время выполнения кода ошибки приводят к созданию и выбрасыванию новых объектов Error.

Данная страница документирует использование объекта Error как самого по себе, так и при использовании в качестве функции-конструктора. Список свойств и методов, унаследованных экземплярами объекта Error, смотрите в разделе Error.prototype (en-US).

Использование как функции

Когда Error используется как функции— без new, она возвращает Error объект. Следовательно простой вызов Error произведёт тот же результат, что и конструктор Error объявленный через new.

// this:
const x = Error('I was created using a function call!');
​​​​// Такая же функциональность:
const y = new Error('I was constructed via the "new" keyword!');

Типы ошибок

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

EvalError

Создаёт экземпляр, представляющий ошибку, возникающую в глобальной функции eval().

InternalError
Non-standard

Создаёт экземпляр, представляющий ошибку, возникающую при выбрасывании внутренней ошибки в движке JavaScript. К примеру, ошибки «слишком глубокая рекурсия» («too much recursion»).

RangeError

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

ReferenceError

Создаёт экземпляр, представляющий ошибку, возникающую при разыменовывании недопустимой ссылки.

SyntaxError

Создаёт экземпляр, представляющий синтаксическую ошибку, возникающую при разборе исходного кода в функции eval().

TypeError

Создаёт экземпляр, представляющий ошибку, возникающую при недопустимом типе для переменной или параметра.

URIError

Создаёт экземпляр, представляющий ошибку, возникающую при передаче в функции encodeURI() или decodeURI() недопустимых параметров.

Свойства

Error.prototype (en-US)

Позволяет добавлять свойства в экземпляры объекта Error.

Методы

Глобальный объект Error не содержит собственных методов, однако, он наследует некоторые методы из цепочки прототипов.

Экземпляры объекта Error

{{page(‘/ru/docs/Web/JavaScript/Reference/Global_Objects/Error/prototype’, ‘Description’)}}

Свойства

{{page(‘/ru/docs/Web/JavaScript/Reference/Global_Objects/Error/prototype’, ‘Properties’)}}

Методы

{{page(‘/ru/docs/Web/JavaScript/Reference/Global_Objects/Error/prototype’, ‘Methods’)}}

Примеры

Пример: выбрасывание обычной ошибки

Обычно, вы создаёте объект Error с намерением возбудить ошибку с помощью ключевого слова throw. Вы можете обработать ошибку с помощью конструкции try...catch:

try {
  throw new Error('Уупс!');
} catch (e) {
  console.log(e.name + ': ' + e.message);
}

Пример: обработка ошибки конкретного типа

Возможно, это следует удалить вы можете обрабатывать только какой-то определённый вид ошибок, проверяя тип ошибки в свойстве constructor или, если вы пишете для современных движков JavaScript, с помощью ключевого слова instanceof:

try {
  foo.bar();
} catch (e) {
  if (e instanceof EvalError) {
    console.log(e.name + ': ' + e.message);
  } else if (e instanceof RangeError) {
    console.log(e.name + ': ' + e.message);
  }
  // ... и т.д.
}

Пример: пользовательские типы ошибок

Вы можете захотеть определить свои собственные типы ошибок, унаследованные от Error, что бы иметь возможность писать throw new MyError() и использовать instanceof MyError для проверки вида ошибки в обработчике исключений. Ниже продемонстрирован общий подход к выполнению этой задачи.

Предупреждение: обратите внимание, что выбрасывание MyError будет сообщать неправильные номер строки lineNumber и имя файла fileName как минимум, в Firefox.

Также смотрите обсуждение «Какой способ расширения Error в JavaScript более предпочтителен?» на Stackoverflow.

// Создаём новый объект, затем через прототип делаем его наследником конструктора Error.
function MyError(message) {
  this.name = 'MyError';
  this.message = message || 'Сообщение по умолчанию';
  this.stack = (new Error()).stack;
}
MyError.prototype = Object.create(Error.prototype);
MyError.prototype.constructor = MyError;

try {
  throw new MyError();
} catch (e) {
  console.log(e.name);     // 'MyError'
  console.log(e.message);  // 'Сообщение по умолчанию'
}

try {
  throw new MyError('пользовательское сообщение');
} catch (e) {
  console.log(e.name);     // 'MyError'
  console.log(e.message);  // 'пользовательское сообщение'
}

Спецификации

Specification
ECMAScript Language Specification
# sec-error-objects

Совместимость с браузерами

BCD tables only load in the browser

Смотрите также

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

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

Этот материал, посвящённый обработке ошибок в JavaScript, разбит на три части. Сначала мы сделаем общий обзор системы обработки ошибок в JavaScript и поговорим об объектах ошибок. После этого мы поищем ответ на вопрос о том, что делать с ошибками, возникающими в серверном коде (в частности, при использовании связки Node.js + Express.js). Далее — обсудим обработку ошибок в React.js. Фреймворки, которые будут здесь рассматриваться, выбраны по причине их огромной популярности. Однако рассматриваемые здесь принципы работы с ошибками универсальны, поэтому вы, даже если не пользуетесь Express и React, без труда сможете применить то, что узнали, к тем инструментам, с которыми работаете.

Код демонстрационного проекта, используемого в данном материале, можно найти в этом репозитории.

1. Ошибки в JavaScript и универсальные способы работы с ними

Если в вашем коде что-то пошло не так, вы можете воспользоваться следующей конструкцией.

throw new Error('something went wrong')

В ходе выполнения этой команды будет создан экземпляр объекта Error и будет сгенерировано (или, как говорят, «выброшено») исключение с этим объектом. Инструкция throw может генерировать исключения, содержащие произвольные выражения. При этом выполнение скрипта остановится в том случае, если не были предприняты меры по обработке ошибки.

Начинающие JS-программисты обычно не используют инструкцию throw. Они, как правило, сталкиваются с исключениями, выдаваемыми либо средой выполнения языка, либо сторонними библиотеками. Когда это происходит — в консоль попадает нечто вроде ReferenceError: fs is not defined и выполнение программы останавливается.

▍Объект Error

У экземпляров объекта Error есть несколько свойств, которыми мы можем пользоваться. Первое интересующее нас свойство — message. Именно сюда попадает та строка, которую можно передать конструктору ошибки в качестве аргумента. Например, ниже показано создание экземпляра объекта Error и вывод в консоль переданной конструктором строки через обращение к его свойству message.

const myError = new Error('please improve your code')
console.log(myError.message) // please improve your code

Второе свойство объекта, очень важное, представляет собой трассировку стека ошибки. Это — свойство stack. Обратившись к нему можно просмотреть стек вызовов (историю ошибки), который показывает последовательность операций, приведшую к неправильной работе программы. В частности, это позволяет понять — в каком именно файле содержится сбойный код, и увидеть, какая последовательность вызовов функций привела к ошибке. Вот пример того, что можно увидеть, обратившись к свойству stack.

Error: please improve your code
 at Object.<anonymous> (/Users/gisderdube/Documents/_projects/hacking.nosync/error-handling/src/general.js:1:79)
 at Module._compile (internal/modules/cjs/loader.js:689:30)
 at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10)
 at Module.load (internal/modules/cjs/loader.js:599:32)
 at tryModuleLoad (internal/modules/cjs/loader.js:538:12)
 at Function.Module._load (internal/modules/cjs/loader.js:530:3)
 at Function.Module.runMain (internal/modules/cjs/loader.js:742:12)
 at startup (internal/bootstrap/node.js:266:19)
 at bootstrapNodeJSCore (internal/bootstrap/node.js:596:3)

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

▍Генерирование и обработка ошибок

Создание экземпляра объекта Error, то есть, выполнение команды вида new Error(), ни к каким особым последствиям не приводит. Интересные вещи начинают происходить после применения оператора throw, который генерирует ошибку. Как уже было сказано, если такую ошибку не обработать, выполнение скрипта остановится. При этом нет никакой разницы — был ли оператор throw использован самим программистом, произошла ли ошибка в некоей библиотеке или в среде выполнения языка (в браузере или в Node.js). Поговорим о различных сценариях обработки ошибок.

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

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

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

const a = 5

try {
    console.log(b) // переменная b не объявлена - возникает ошибка
} catch (err) {
    console.error(err) // в консоль попадает сообщение об ошибке и стек ошибки
}

console.log(a) // выполнение скрипта не останавливается, данная команда выполняется

Если бы в этом примере мы не заключили бы сбойную команду console.log(b) в блок try...catch, то выполнение скрипта было бы остановлено.

▍Блок finally

Иногда случается так, что некий код нужно выполнить независимо от того, произошла ошибка или нет. Для этого можно, в конструкции try...catch, использовать третий, необязательный, блок — finally. Часто его использование эквивалентно некоему коду, который идёт сразу после try...catch, но в некоторых ситуациях он может пригодиться. Вот пример его использования.

const a = 5

try {
    console.log(b) // переменная b не объявлена - возникает ошибка
} catch (err) {
    console.error(err) // в консоль попадает сообщение об ошибке и стек ошибки
} finally {
    console.log(a) // этот код будет выполнен в любом случае
}

▍Асинхронные механизмы — коллбэки

Программируя на JavaScript всегда стоит обращать внимание на участки кода, выполняющиеся асинхронно. Если у вас имеется асинхронная функция и в ней возникает ошибка, скрипт продолжит выполняться. Когда асинхронные механизмы в JS реализуются с использованием коллбэков (кстати, делать так не рекомендуется), соответствующий коллбэк (функция обратного вызова) обычно получает два параметра. Это нечто вроде параметра err, который может содержать ошибку, и result — с результатами выполнения асинхронной операции. Выглядит это примерно так:

myAsyncFunc(someInput, (err, result) => {
    if(err) return console.error(err) // порядок работы с объектом ошибки мы рассмотрим позже
    console.log(result)
})

Если в коллбэк попадает ошибка, она видна там в виде параметра err. В противном случае в этот параметр попадёт значение undefined или null. Если оказалось, что в err что-то есть, важно отреагировать на это, либо так как в нашем примере, воспользовавшись командой return, либо воспользовавшись конструкцией if...else и поместив в блок else команды для работы с результатом выполнения асинхронной операции. Речь идёт о том, чтобы, в том случае, если произошла ошибка, исключить возможность работы с результатом, параметром result, который в таком случае может иметь значение undefined. Работа с таким значением, если предполагается, например, что оно содержит объект, сама может вызвать ошибку. Скажем, это произойдёт при попытке использовать конструкцию result.data или подобную ей.

▍Асинхронные механизмы — промисы

Для выполнения асинхронных операций в JavaScript лучше использовать не коллбэки а промисы. Тут, в дополнение к улучшенной читабельности кода, имеются и более совершенные механизмы обработки ошибок. А именно, возиться с объектом ошибки, который может попасть в функцию обратного вызова, при использовании промисов не нужно. Здесь для этой цели предусмотрен специальный блок catch. Он перехватывает все ошибки, произошедшие в промисах, которые находятся до него, или все ошибки, которые произошли в коде после предыдущего блока catch. Обратите внимание на то, что если в промисе произошла ошибка, для обработки которой нет блока catch, это не остановит выполнение скрипта, но сообщение об ошибке будет не особенно удобочитаемым.

(node:7741) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: something went wrong
(node:7741) DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code. */

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

Promise.resolve(1)
    .then(res => {
        console.log(res) // 1

        throw new Error('something went wrong')

        return Promise.resolve(2)
    })
    .then(res => {
        console.log(res) // этот блок выполнен не будет
    })
    .catch(err => {
        console.error(err) // о том, что делать с этой ошибкой, поговорим позже
        return Promise.resolve(3)
    })
    .then(res => {
        console.log(res) // 3
    })
    .catch(err => {
        // этот блок тут на тот случай, если в предыдущем блоке возникнет какая-нибудь ошибка
        console.error(err)
    })

▍Асинхронные механизмы и try…catch

После того, как в JavaScript появилась конструкция async/await, мы вернулись к классическому способу обработки ошибок — к try...catch...finally. Обрабатывать ошибки при таком подходе оказывается очень легко и удобно. Рассмотрим пример.

;(async function() {
    try {
        await someFuncThatThrowsAnError()
    } catch (err) {
        console.error(err) // об этом поговорим позже
    }

    console.log('Easy!') // будет выполнено
})()

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

2. Генерирование и обработка ошибок в серверном коде

Теперь, когда у нас есть инструменты для работы с ошибками, посмотрим на то, что мы можем с ними делать в реальных ситуациях. Генерирование и правильная обработка ошибок — это важнейший аспект серверного программирования. Существуют разные подходы к работе с ошибками. Здесь будет продемонстрирован подход с использованием собственного конструктора для экземпляров объекта Error и кодов ошибок, которые удобно передавать во фронтенд или любым механизмам, использующим серверные API. Как структурирован бэкенд конкретного проекта — особого значения не имеет, так как при любом подходе можно использовать одни и те же идеи, касающиеся работы с ошибками.

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

  1. Универсальная обработка ошибок — некий базовый механизм, подходящий для обработки любых ошибок, в ходе работы которого просто выдаётся сообщение наподобие Something went wrong, please try again or contact us, предлагающее пользователю попробовать выполнить операцию, давшую сбой, ещё раз или связаться с владельцем сервера. Эта система не отличается особой интеллектуальностью, но она, по крайней мере, способна сообщить пользователю о том, что что-то пошло не так. Подобное сообщение гораздо лучше, чем «бесконечная загрузка» или нечто подобное.
  2. Обработка конкретных ошибок — механизм, позволяющий сообщить пользователю подробные сведения о причинах неправильного поведения системы и дать ему конкретные советы по борьбе с неполадкой. Например, это может касаться отсутствия неких важных данных в запросе, который пользователь отправляет на сервер, или в том, что в базе данных уже существует некая запись, которую он пытается добавить ещё раз, и так далее.

▍Разработка собственного конструктора объектов ошибок

Здесь мы воспользуемся стандартным классом Error и расширим его. Пользоваться механизмами наследования в JavaScript — дело рискованное, но в данном случае эти механизмы оказываются весьма полезными. Зачем нам наследование? Дело в том, что нам, для того, чтобы код удобно было бы отлаживать, нужны сведения о трассировке стека ошибки. Расширяя стандартный класс Error, мы, без дополнительных усилий, получаем возможности по трассировке стека. Мы добавляем в наш собственный объект ошибки два свойства. Первое — это свойство code, доступ к которому можно будет получить с помощью конструкции вида err.code. Второе — свойство status. В него будет записываться код состояния HTTP, который планируется передавать клиентской части приложения.

Вот как выглядит класс CustomError, код которого оформлен в виде модуля.

class CustomError extends Error {
    constructor(code = 'GENERIC', status = 500, ...params) {
        super(...params)

        if (Error.captureStackTrace) {
            Error.captureStackTrace(this, CustomError)
        }

        this.code = code
        this.status = status
    }
}

module.exports = CustomError

▍Маршрутизация

Теперь, когда наш объект ошибки готов к использованию, нужно настроить структуру маршрутов. Как было сказано выше, нам требуется реализовать унифицированный подход к обработке ошибок, позволяющий одинаково обрабатывать ошибки для всех маршрутов. По умолчанию фреймворк Express.js не вполне поддерживает такую схему работы. Дело в том, что все его маршруты инкапсулированы.

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

{
    error: 'SOME_ERROR_CODE',
    description: 'Something bad happened. Please try again or contact support.'
}

Если на данном этапе происходящие кажется вам непонятным — не беспокойтесь — просто продолжайте читать, пробуйте работать с тем, о чём идёт речь, и постепенно вы во всём разберётесь. На самом деле, если говорить о компьютерном обучении, здесь применяется подход «сверху-вниз», когда сначала обсуждаются общие идеи, а потом осуществляется переход к частностям.

Вот как выглядит код обработчика маршрутов.

const express = require('express')
const router = express.Router()
const CustomError = require('../CustomError')

router.use(async (req, res) => {
    try {
        const route = require(`.${req.path}`)[req.method]

        try {
            const result = route(req) // Передаём запрос функции route
            res.send(result) // Передаём клиенту то, что получено от функции route
        } catch (err) {
            /*
            Сюда мы попадаем в том случае, если в функции route произойдёт ошибка
            */
            if (err instanceof CustomError) {
                /* 
                Если ошибка уже обработана - трансформируем её в 
                возвращаемый объект
                */

                return res.status(err.status).send({
                    error: err.code,
                    description: err.message,
                })
            } else {
                console.error(err) // Для отладочных целей

                // Общая ошибка - вернём универсальный объект ошибки
                return res.status(500).send({
                    error: 'GENERIC',
                    description: 'Something went wrong. Please try again or contact support.',
                })
            }
        }
    } catch (err) {
        /* 
         Сюда мы попадём, если запрос окажется неудачным, то есть,
         либо не будет найдено файла, соответствующего пути, переданному
         в запросе, либо не будет экспортированной функции с заданным
         методом запроса
        */
        res.status(404).send({
            error: 'NOT_FOUND',
            description: 'The resource you tried to access does not exist.',
        })
    }
})

module.exports = router

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

Теперь взглянем на файл маршрутов.

const CustomError = require('../CustomError')

const GET = req => {
    // пример успешного выполнения запроса
    return { name: 'Rio de Janeiro' }
}

const POST = req => {
    // пример ошибки общего характера
    throw new Error('Some unexpected error, may also be thrown by a library or the runtime.')
}

const DELETE = req => {
    // пример ошибки, обрабатываемой особым образом
    throw new CustomError('CITY_NOT_FOUND', 404, 'The city you are trying to delete could not be found.')
}

const PATCH = req => {
    // пример перехвата ошибок и использования CustomError
    try {
        // тут случилось что-то нехорошее
        throw new Error('Some internal error')
    } catch (err) {
        console.error(err) // принимаем решение о том, что нам тут делать

        throw new CustomError(
            'CITY_NOT_EDITABLE',
            400,
            'The city you are trying to edit is not editable.'
        )
    }
}

module.exports = {
    GET,
    POST,
    DELETE,
    PATCH,
}

В этих примерах с самими запросами ничего не делается. Тут просто рассматриваются разные сценарии возникновения ошибок. Итак, например, запрос GET /city попадёт в функцию const GET = req =>..., запрос POST /city попадёт в функцию const POST = req =>... и так далее. Эта схема работает и при использовании параметров запросов. Например — для запроса вида GET /city?startsWith=R. В целом, здесь продемонстрировано, что при обработке ошибок, во фронтенд может попасть либо общая ошибка, содержащая лишь предложение попробовать снова или связаться с владельцем сервера, либо ошибка, сформированная с использованием конструктора CustomError, которая содержит подробные сведения о проблеме.
Данные общей ошибки придут в клиентскую часть приложения в таком виде:

{
    error: 'GENERIC',
    description: 'Something went wrong. Please try again or contact support.'
}

Конструктор CustomError используется так:

throw new CustomError('MY_CODE', 400, 'Error description')

Это даёт следующий JSON-код, передаваемый во фронтенд:

{
    error: 'MY_CODE',
    description: 'Error description'
}

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

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

3. Работа с ошибками на клиенте

Теперь пришла пора описать третью часть нашей системы обработки ошибок, касающуюся фронтенда. Тут нужно будет, во-первых, обрабатывать ошибки, возникающие в клиентской части приложения, а во-вторых, понадобится оповещать пользователя об ошибках, возникающих на сервере. Разберёмся сначала с показом сведений о серверных ошибках. Как уже было сказано, в этом примере будет использована библиотека React.

▍Сохранение сведений об ошибках в состоянии приложения

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

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

  1. Глобальные ошибки — в эту категорию попадают сообщения об ошибках общего характера, приходящие с сервера, или ошибки, которые, например, возникают в том случае, если пользователь не вошёл в систему и в других подобных ситуациях.
  2. Специфические ошибки, выдаваемые серверной частью приложения — сюда относятся ошибки, сведения о которых приходят с сервера. Например, подобная ошибка возникает, если пользователь попытался войти в систему и отправил на сервер имя и пароль, а сервер сообщил ему о том, что пароль неправильный. Подобные вещи в клиентской части приложения не проверяются, поэтому сообщения о таких ошибках должны приходить с сервера.
  3. Специфические ошибки, выдаваемые клиентской частью приложения. Пример такой ошибки — сообщение о некорректном адресе электронной почты, введённом в соответствующее поле.

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

Здесь будет использоваться встроенная в React система управления состоянием приложения, но, при необходимости, вы можете воспользоваться и специализированными решениями для управления состоянием — такими, как MobX или Redux.

▍Глобальные ошибки

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

Сообщение о глобальной ошибке

Теперь взглянем на код, который хранится в файле Application.js.

import React, { Component } from 'react'

import GlobalError from './GlobalError'

class Application extends Component {
    constructor(props) {
        super(props)

        this.state = {
            error: '',
        }

        this._resetError = this._resetError.bind(this)
        this._setError = this._setError.bind(this)
    }

    render() {
        return (
            <div className="container">
                <GlobalError error={this.state.error} resetError={this._resetError} />
                <h1>Handling Errors</h1>
            </div>
        )
    }

    _resetError() {
        this.setState({ error: '' })
    }

    _setError(newError) {
        this.setState({ error: newError })
    }
}

export default Application

Как видно, в состоянии, в Application.js, имеется место для хранения данных ошибки. Кроме того, тут предусмотрены методы для сброса этих данных и для их изменения.

Ошибка и метод для сброса ошибки передаётся компоненту GlobalError, который отвечает за вывод сообщения об ошибке на экран и за сброс ошибки после нажатия на значок x в поле, где выводится сообщение. Вот код компонента GlobalError (файл GlobalError.js).

import React, { Component } from 'react'

class GlobalError extends Component {
    render() {
        if (!this.props.error) return null

        return (
            <div
                style={{
                    position: 'fixed',
                    top: 0,
                    left: '50%',
                    transform: 'translateX(-50%)',
                    padding: 10,
                    backgroundColor: '#ffcccc',
                    boxShadow: '0 3px 25px -10px rgba(0,0,0,0.5)',
                    display: 'flex',
                    alignItems: 'center',
                }}
            >
                {this.props.error}
                 
                <i
                    className="material-icons"
                    style={{ cursor: 'pointer' }}
                    onClick={this.props.resetError}
                >
                    close
                </font></i>
            </div>
        )
    }
}

export default GlobalError

Обратите внимание на строку if (!this.props.error) return null. Она указывает на то, что при отсутствии ошибки компонент ничего не выводит. Это предотвращает постоянный показ красного прямоугольника на странице. Конечно, вы, при желании, можете поменять внешний вид и поведение этого компонента. Например, вместо того, чтобы сбрасывать ошибку по нажатию на x, можно задать тайм-аут в пару секунд, по истечении которого состояние ошибки сбрасывается автоматически.

Теперь, когда всё готово для работы с глобальными ошибками, для задания глобальной ошибки достаточно воспользоваться _setError из Application.js. Например, это можно сделать в том случае, если сервер, после обращения к нему, вернул сообщение об общей ошибке (error: 'GENERIC'). Рассмотрим пример (файл GenericErrorReq.js).

import React, { Component } from 'react'
import axios from 'axios'

class GenericErrorReq extends Component {
    constructor(props) {
        super(props)

        this._callBackend = this._callBackend.bind(this)
    }

    render() {
        return (
            <div>
                <button onClick={this._callBackend}>Click me to call the backend</button>
            </div>
        )
    }

    _callBackend() {
        axios
            .post('/api/city')
            .then(result => {
                // сделать что-нибудь с результатом в том случае, если запрос оказался успешным
            })
            .catch(err => {
                if (err.response.data.error === 'GENERIC') {
                    this.props.setError(err.response.data.description)
                }
            })
    }
}

export default GenericErrorReq

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

▍Обработка специфических ошибок, возникающих при выполнении запросов

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

Сообщение о специфической ошибке

Тут используется тот же принцип, который мы применяли при работе с глобальными ошибками. Только сведения о таких ошибках хранятся в локальном состоянии соответствующих компонентов. Работа с ними очень похожа на работу с глобальными ошибками. Вот код файла SpecificErrorReq.js.

import React, { Component } from 'react'
import axios from 'axios'

import InlineError from './InlineError'

class SpecificErrorRequest extends Component {
    constructor(props) {
        super(props)

        this.state = {
            error: '',
        }

        this._callBackend = this._callBackend.bind(this)
    }

    render() {
        return (
            <div>
                <button onClick={this._callBackend}>Delete your city</button>
                <InlineError error={this.state.error} />
            </div>
        )
    }

    _callBackend() {
        this.setState({
            error: '',
        })

        axios
            .delete('/api/city')
            .then(result => {
                // сделать что-нибудь с результатом в том случае, если запрос оказался успешным
            })
            .catch(err => {
                if (err.response.data.error === 'GENERIC') {
                    this.props.setError(err.response.data.description)
                } else {
                    this.setState({
                        error: err.response.data.description,
                    })
                }
            })
    }
}

export default SpecificErrorRequest

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

▍Ошибки, возникающие в клиентской части приложения

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

В поле ничего нет, мы сообщаем об этом пользователю

Вот код файла SpecificErrorFrontend.js, реализующий вышеописанный функционал.

import React, { Component } from 'react'
import axios from 'axios'

import InlineError from './InlineError'

class SpecificErrorRequest extends Component {
    constructor(props) {
        super(props)

        this.state = {
            error: '',
            city: '',
        }

        this._callBackend = this._callBackend.bind(this)
        this._changeCity = this._changeCity.bind(this)
    }

    render() {
        return (
            <div>
                <input
                    type="text"
                    value={this.state.city}
                    style={{ marginRight: 15 }}
                    onChange={this._changeCity}
                />
                <button onClick={this._callBackend}>Delete your city</button>
                <InlineError error={this.state.error} />
            </div>
        )
    }

    _changeCity(e) {
        this.setState({
            error: '',
            city: e.target.value,
        })
    }

    _validate() {
        if (!this.state.city.length) throw new Error('Please provide a city name.')
    }

    _callBackend() {
        this.setState({
            error: '',
        })

        try {
            this._validate()
        } catch (err) {
            return this.setState({ error: err.message })
        }

        axios
            .delete('/api/city')
            .then(result => {
                // сделать что-нибудь с результатом в том случае, если запрос оказался успешным
            })
            .catch(err => {
                if (err.response.data.error === 'GENERIC') {
                    this.props.setError(err.response.data.description)
                } else {
                    this.setState({
                        error: err.response.data.description,
                    })
                }
            })
    }
}

export default SpecificErrorRequest

▍Интернационализация сообщений об ошибках с использованием кодов ошибок

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

Итоги

Надеемся, теперь у вас сформировалось понимание того, как можно работать с ошибками в веб-приложениях. Нечто вроде console.error(err) следует использовать только в отладочных целях, в продакшн подобные вещи, забытые программистом, проникать не должны. Упрощает решение задачи логирования использование какой-нибудь подходящей библиотеки наподобие loglevel.

Уважаемые читатели! Как вы обрабатываете ошибки в своих проектах?

Оператор throw позволяет создать исключение или пользовательскую ошибку. Исключением может быть строка Javascript, число, логическое значение или объект. Итак, если вы используете этот оператор вместе с оператором try…catch. Это позволяет вам контролировать поток программы и генерировать точные сообщения об ошибках. Например: кинуть «слишком мало» (кинуть текст), кинуть 500 (кинуть цифру) и т. д.

Прежде всего, throw Error() и throw new Error() функционально эквивалентны.

Синтаксис:

throw Error("Enter your error message here");

Объект ошибки: объект, содержащий информацию об ошибке, генерируется и передается в функцию catch в качестве аргумента. Объект ошибки имеет три свойства: имя, сообщение и стек.

Конструктор Error() используется для создания нового объекта ошибки.

Пример: Для лучшего понимания возьмем пример:

Javascript

try {

    throw Error("msg");

} catch (e) {

    console.log("for Error()");

    console.log(e);

}

Выход:

В приведенном выше коде я добавил оператор try…catch с функцией throw Error(). Этот код сделает так, что ошибка «msg» будет выброшена в блоке try, который будет выполняться операторами catch.

новый объект Error: фиксирует несколько свойств места, где произошла ошибка. Он выдает событие ошибки с двумя параметрами: имя и сообщение. Это также прекращает дальнейшее выполнение.

Пример: Для лучшего понимания возьмем пример :

Javascript

try {

    throw new Error("msg");

} catch (e) {

    console.log("for new Error()");

    console.log(e);

}

Выход :

В приведенном выше коде я добавил оператор try…catch с функцией throw new Error(). Этот код сделает так, что ошибка «msg» будет выброшена в блоке try, который будет выполняться операторами catch.

Как вы можете видеть на изображениях обеих консолей, выдача ошибки (‘msg’) и выдача новой ошибки (‘msg’) дают одинаковые результаты.

Примечание: Error() и new Error() в результате возвращают объект.

Разница между throw Error() и throw new Error():

бросить ошибку ()

выдать новую ошибку()

Ошибка(…) менее надежна и последовательна. new Error(…) более надежен и последователен.
Объект Error содержит трассировку стека и другую полезную отладочную информацию, которая теряется при использовании строкового литерала. Создание объектов с использованием классов ES6 требует использования new и расширения Error через класс — единственный способ сохранить трассировку стека.
Мы можем создать исключение или пользовательскую ошибку, используя throw Error() Новый метод Error() — это встроенный интерфейс прикладного программирования модуля Node.
throw Error() похож на строку Javascript, число, логическое значение или объект. Он возвращает определенные ошибки, как определено в значении сообщения, которое передается в качестве аргумента.
Мы можем контролировать поток программы и генерировать точные сообщения об ошибках, используя throw Error() . Он создает новый объект Error и устанавливает для свойства error.message предоставленное текстовое сообщение.
Оператор throw генерирует исключение.

Его синтаксис -:

новая ошибка (сообщение)

РЕКОМЕНДУЕМЫЕ СТАТЬИ

Cover image for Exception handling in JavaScript

Brian Neville-O'Neill

Written by Deepak Gupta✏️

Errors are part of the programming journey. By producing errors, we actually learn how not to do something and how to do it better next time.

In JavaScript, when code statements are tightly coupled and one generates an error, it makes no sense to continue with the remaining code statements. Instead, we try to recover from the error as gracefully as we can. The JavaScript interpreter checks for exception handling code in case of such errors, and if there is no exception handler, the program returns whatever function caused the error.

This is repeated for each function on the call stack until an exception handler is found or the top-level function is reached, which causes the program to terminate with an error.

In general, exceptions are handled in two ways:

  1. Throw an exception — If there is a problem that can’t be handled meaningfully where it occurs at runtime, it’s best to throw it
function openFile(fileName) {
    if (!exists(fileName)) {
        throw new Error('Could not find file '+fileName); // (1)
    }
    ...
}

Enter fullscreen mode

Exit fullscreen mode

  1. Catch an exception —  Thrown exceptions are caught and handled at the place where they make more sense at runtime
try {
  openFile('../test.js');
} catch(e) {
// gracefully handled the thrown expection 
}

Enter fullscreen mode

Exit fullscreen mode

Let’s dive into these actions in more detail.

LogRocket Free Trial Banner

Throw an exception

If you’ve been using JavaScript for a long time, you may have seen something like ReferenceError: fs is not defined. This represents an exception that was thrown via a throw statement.

Syntax

throw «value»;
// Don't do this
if (somethingBadHappened) {
    throw 'Something bad happened';
}

Enter fullscreen mode

Exit fullscreen mode

There is no restriction on the type of data that can be thrown as an exception, but JavaScript has special built-in exception types. One of them is Error, as you saw in the previous example. These built-in exception types give us more details than just a message for an exception.

Error

The Error type is used to represent generic exceptions. This type of exception is most often used for implementing user-defined exceptions. It has two built-in properties to use.

1. message

This is what we pass as an argument to the Error constructor — e.g., new Error('This is the message'). You can access the message through the message property.

const myError = new Error(Error is created)
console.log(myError.message) // Error is created

Enter fullscreen mode

Exit fullscreen mode

2. stack

The stack property returns the history (call stack) of what files were responsible for causing the error. The stack also includes the message at the top and is followed by the actual stack, starting with the most recent/isolated point of the error to the most outward responsible file.

Error: Error is created
at Object. (/Users/deepak/Documents/error-handling/src/index.js:1:79)
at Module.compile (internal/modules/cjs/loader.js:689:30)
at Object.Module.extensions..js (internal/modules/cjs/loader.js:700:10)
at Module.load (internal/modules/cjs/loader.js:599:32)
at tryModuleLoad (internal/modules/cjs/loader.js:538:12)
at Function.Module._load (internal/modules/cjs/loader.js:530:3)
at Function.Module.runMain (internal/modules/cjs/loader.js:742:12)
at startup (internal/bootstrap/node.js:266:19)
at bootstrapNodeJSCore (internal/bootstrap/node.js:596:3)

Enter fullscreen mode

Exit fullscreen mode

Note: new Error('...') does not do anything until it’s thrown — i.e., throw new Error('error msg')   will create an instance of an Error in JavaScript and stop the execution of your script unless you do something with the Error, such as catch it.

Catch an exception

Now that we know what exceptions are and how to throw them, let’s discuss how to stop them from crashing our programs by catching them.

try-catch-finally

This is the simplest way to handle the exceptions. Let’s look at the syntax.

try {
    // Code to run
  } catch (e) {
    // Code to run if an exception occurs
  }
  [ // optional
    finally {
      // Code that is always executed regardless of 
      // an exception occurring
    }
  ]

Enter fullscreen mode

Exit fullscreen mode

In the try clause, we add code that could potentially generate exceptions. If an exception occurs, the catch clause is executed.

Sometimes it is necessary to execute code whether or not it generates an exception. Then we can use the optional block finally.

The finally block will execute even if the try or catch clause executes a return statement. For example, the following function returns false because the finally clause is the last thing to execute.

function foo() {
  try {
    return true;
  } finally {
    return false;
  }
}

Enter fullscreen mode

Exit fullscreen mode

We use try-catch in places where we can’t check the correctness of code beforehand.

const user = '{"name": "Deepak gupta", "age": 27}';
try {
  // Code to run
  JSON.parse(params)
  // In case of error, the rest of code will never run
  console.log(params)
} catch (err) {
  // Code to run in case of exception
  console.log(err.message)
}

Enter fullscreen mode

Exit fullscreen mode

As shown above, it’s impossible to check the JSON.parse to have the stringify object or a string before the execution of the code.

Note: You can catch programmer-generated and runtime exceptions, but you can’t catch JavaScript syntax errors.

try-catch-finally can only catch synchronous errors. If we try to use it with asynchronous code, it’s possible that try-catch-finally will have already been executed before the asynchronous code finishes its execution.

How to handle exceptions in an asynchronous code block

JavaScript provides a few ways to handle exceptions in an asynchronous code block.

Callback functions

With callback functions (not recommended) ,  we usually receive two parameters that look something like this:

asyncfunction(code, (err, result) => {
    if(err) return console.error(err);
    console.log(result);
})

Enter fullscreen mode

Exit fullscreen mode

We can see that there are two arguments: err and result. If there is an error, the err parameter will be equal to that error, and we can throw the error to do exception handling.

It is important to either return something in the if(err) block or wrap your other instruction in an else block. Otherwise, you might get another error — e.g., result might be undefined when you try to access result.data.

Promises

With promises — then or catch — we can process errors by passing an error handler to the then method or using a catch clause.

promise.then(onFulfilled, onRejected)

Enter fullscreen mode

Exit fullscreen mode

It’s also possible to add an error handler with .catch(onRejected) instead of .then(null, onRejected), which works the same way.

Let’s look at a .catch example of promise rejection.

Promise.resolve('1')
  .then(res => {
      console.log(res) // 1
      throw new Error('something went wrong'); // exception thrown 
})
.then(res => {
      console.log(res) // will not get executed
})
.catch(err => { 
      console.error(err) // exception catched and handled
});

Enter fullscreen mode

Exit fullscreen mode

async and await with try-catch

With async/await and try-catch-finally, handling exceptions is a breeze.

async function() {
    try {
        await someFuncThatThrowsAnError()
    } catch (err) {
        console.error(err) 
    }
})

Enter fullscreen mode

Exit fullscreen mode

How to handle uncaught exceptions

Now that we have a good understanding of how to perform exception handling in synchronous and asynchronous code blocks, let’s answer the last burning question of this article : how do we handle uncaught exceptions?

In the browser

The method window.onerror() causes the error event to be fired on the window object whenever an error occurs during runtime. We can use this method to handle the uncaught exception.

Another utility mode for onerror() is using it to display a message in case there is an error when loading images in your site.

<img src="testt.jpg" onerror="alert('An error occurred loading yor photo.')" />

Enter fullscreen mode

Exit fullscreen mode

On a Node.js server

The process object derived from the EventEmitter module can be subscribed to the event uncaughtException.

process.on("uncaughtException", () => {})`

Enter fullscreen mode

Exit fullscreen mode

We can pass a callback to handle the exception. If we try to catch this uncaught exception, the process won’t terminate, so we have to do it manually.

The uncaughtException only works with synchronous code. For asynchronous code, there is another event called unhandledRejection.

process.on("unhandledRejection", () => {})

Enter fullscreen mode

Exit fullscreen mode

Never try to implement a catch-all handler for the base Error type. This will obfuscate whatever happened and compromise the maintainability and extensibility of your code.

Key takeaways

Let’s review some of the main points we discussed in this article.

  • The throw statement is used to generate user-defined exceptions. During runtime, when a throw statement is encountered, execution of the current function will stop and control will be passed to the first catch clause in the call stack. If there is no catch clause, the program will terminate
  • JavaScript has some built-in exception types, most notably Error, which returns the error stack and message
  • The try clause will contain code that could potentially generate an exception
  • The catch clause will be executed when exceptions occur
  • For asynchronous code, it’s best to use async-await with try-catch
  • An unhandled exception can be caught, which can prevent the app from crashing

When done properly throughout, exception handling can help you improve the maintainability, extensibility, and readability of your code.


Plug: LogRocket, a DVR for web apps

 

LogRocket Dashboard Free Trial Banner

 
LogRocket is a frontend logging tool that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

 
In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.

 
Try it for free.


The post Exception handling in JavaScript appeared first on LogRocket Blog.

Существует ряд причин, по которым код JavaScript может вызывать ошибки, например:

  • Проблема с сетевым подключением;
  • Пользователь мог ввести неверное значение в поля формы;
  • Ссылка на объекты или функции, которые не существуют;
  • Неправильные данные отправляются или принимаются с веб-сервера;
  • Служба, к которой приложение должно получить доступ, может быть временно недоступна.

Эти типы ошибок известны как ошибки времени выполнения (runtime errors), поскольку они возникают во время выполнения скрипта. Профессиональное приложение должно иметь возможность корректно обрабатывать такие ошибки во время выполнения. Обычно это означает понятное информирование пользователя о возникшей проблеме.

Оператор try…catch

JavaScript предоставляет оператор try-catch, чтобы перехватывать ошибки времени выполнения и корректно их обработать.

Любой код, который может вызвать ошибку, должен быть помещен в блок оператора try, а код для обработки ошибки помещен в блок catch, как показано здесь:

try {
    // Код, который может вызвать ошибку
} catch(error) {
    // Действие, которое нужно выполнить при возникновении ошибки
}

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

Следующий пример демонстрирует, как работает оператор try-catch:

try {
    var greet = "Hi, there!";
    document.write(greet);
    
    // Попытка получить доступ к несуществующей переменной
    document.write(welcome);
    
    // Если произошла ошибка, следующая строка не будет выполнена
    alert("All statements are executed successfully.");
} catch(error) {
    // Обработка ошибки
  alert("Caught error: " + error.message);
}
 
// Продолжаем исполнение кода
document.write("<p>Hello World!</p>");

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

Также обратите внимание, что за ключевым словом catch указывается идентификатор в скобках. Этот идентификатор действует как параметр функции. При возникновении ошибки интерпретатор JavaScript генерирует объект, содержащий сведения о нем. Этот объект ошибки затем передается в качестве аргумента для обработки.

Оператор try-catch является механизмом обработки исключений. Исключением является сигнал, который указывает, что во время выполнения программы возникли какие-то исключительные условия или ошибки. Термины «исключение» и «ошибка» часто используются взаимозаменяемо.

Оператор try…catch…finally

Оператор try-catch также может содержать предложение finally. Код внутри блока finally всегда будет выполняться независимо от того, произошла ошибка в блоке try или нет.

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

// Присвоение значения, возвращаемого диалоговым окном
var num = prompt("Enter a positive integer between 0 to 100");

// Запоминание времени начала исполнения
var start = Date.now();

try {
    if(num > 0 && num <= 100) {
        alert(Math.pow(num, num)); // the base to the exponent power
    } else {
        throw new Error("An invalid value is entered!");
    }
} catch(e) {
    alert(e.message);
} finally {
    // Отображение времени, необходимого для выполнения кода
    alert("Execution took: " + (Date.now() - start) + "ms");
}

Вызов ошибок с помощью оператора throw

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

Общий синтаксис оператора throw: throw expression;

Выражение expression может быть объектом или значением любого типа данных. Однако лучше использовать объекты, желательно со свойствами name и message. Встроенный в JavaScript конструктор Error() предоставляет удобный способ создания объекта ошибки. Давайте посмотрим на некоторые примеры:

throw 123;
throw "Missing values!";
throw true;
throw { name: "InvalidParameter", message: "Parameter is not a number!" };
throw new Error("Something went wrong!");

Если вы используете встроенные в JavaScript функции конструктора ошибок (например, Error(), TypeError() и т. д.) для создания объектов ошибок, тогда свойство name совпадает с именем конструктора, а message равно аргументу функции конструктора.

Теперь мы собираемся создать функцию squareRoot(), чтобы найти квадратный корень числа. Это можно сделать просто с помощью встроенной в JavaScript функции Math.sqrt(), но проблема здесь в том, что она возвращает NaN для отрицательных чисел, не давая никаких подсказок о том, что пошло не так.

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

function squareRoot(number) {
    // Выдает ошибку, если число отрицательное
    if(number < 0) {
        throw new Error("Sorry, can't calculate square root of a negative number.");
    } else {
        return Math.sqrt(number);
    }
}
    
try {
    squareRoot(16);
    squareRoot(625);
    squareRoot(-9);
    squareRoot(100);
    
    // Если выдается ошибка, следующая строка не будет выполнена
    alert("All calculations are performed successfully.");
} catch(e) {
    // Обработка ошибки
    alert(e.message);
}

Теоретически можно вычислить квадратный корень из отрицательного числа, используя мнимое число i, где i2 = -1. Следовательно, квадратный корень из -4 равен 2i, квадратный корень из -9 равен 3i и так далее. Но мнимые числа не поддерживаются в JavaScript.

Типы ошибок

Объект Error является базовым типом всех ошибок и имеет два основных свойства: name, указывающее тип ошибки и свойство message, которое содержит сообщение, описывающее ошибку более подробно. Любая выданная ошибка будет экземпляром объекта Error.

Существует несколько различных типов ошибок, которые могут возникнуть во время выполнения программы JavaScript, например RangeError, ReferenceError, SyntaxError, TypeError, и URIError.

В следующем разделе описывается каждый из этих типов ошибок более подробно:

RangeError

RangeError генерируется, когда вы используете число, выходящее за пределы допустимых значений. Например, создание массива с отрицательной длиной вызовет RangeError.

var num = 12.735;
num.toFixed(200); // выдает ошибку диапазона (допустимый диапазон от 0 до 100)

var array = new Array(-1); // выдает ошибку диапазона

ReferenceError

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

var firstName = "Harry";
console.log(firstname); // выдает ошибку ссылки (имена переменных чувствительны к регистру)

undefinedObj.getValues(); // выдает ошибку ссылки

nonexistentArray.length; // выдает ошибку ссылки

SyntaxError

SyntaxError генерируется, если в вашем коде JavaScript есть какие-либо синтаксические проблемы. Например, если закрывающая скобка отсутствует, циклы не структурированы должным образом и т. д.

var array = ["a", "b", "c"];
document.write(array.slice(2); // выдает синтаксическую ошибку (отсутствует скобка)

alert("Hello World!'); // выдает синтаксическую ошибку (несоответствие кавычек)

TypeError

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

var num = 123;
num.toLowerCase(); /* выдает ошибку (поскольку toLowerCase() является строковым методом, число не может быть преобразовано в нижний регистр) */

var greet = "Hello World!"
greet.join() // выдает ошибку (так как join() является методом массива)

URIError

URIError генерируется, когда вы указали недопустимый URI (расшифровывается как Uniform Resource Identifier) для функций, связанных с URI, таких как encodeURI() или decodeURI(), как показано здесь:

var a = "%E6%A2%B";
decodeURI(a);  // выдает ошибку URI

var b = "uD800";
encodeURI(b);   // выдает ошибку URI

Существует еще один тип ошибки EvalError, который генерируется при возникновении ошибки во время выполнения кода с помощью функции eval(). Хотя эта ошибка больше не генерируется JavaScript, этот объект все еще остается для обратной совместимости.

Конкретный тип ошибки также может быть выдан вручную с использованием соответствующего конструктора и оператора throw. Например, чтобы сгенерировать ошибку TypeError, вы можете использовать конструктор TypeError(), например:

var num = prompt("Please enter a number");

try {
    if(num != "" && num !== null && isFinite(+num)) {
        alert(Math.exp(num));
    } else {
        throw new TypeError("You have not entered a number.");
    }
} catch(e) {
    alert(e.name);
    alert(e.message);
    alert(e.stack); // нестандартное свойство
}

Объект Error также поддерживает некоторые нестандартные свойства. Одним из наиболее широко используемых таких свойств является: stack trace, который возвращает трассировку стека для этой ошибки. Вы можете использовать его в целях отладки, но не используйте его на рабочих сайтах.

Понравилась статья? Поделить с друзьями:
  • Throw new error library dir does not exist libdir
  • Throw new error jquery requires a window with a document
  • Throw new error jest
  • Throw new error in promise
  • Throw new error cheerio load expects a string