Главная » Ошибки » Обработка ошибок и исключений в Node

Обработка ошибок и исключений в Node

Иван Теленков

Работаю в ИТ 6+ лет в сфере разработки ПО для мобильных устройств. Преимущественно работаю с экосистемой Apple. Разрабатываю для iPhone, iPad, iWatch.
Теоретический минимум при работе с исключениями: как устроен объект Error, стек вызовов, нюансы работы с исключениями в синхронном и асинхронном коде.️ Лучшие практики обработки ошибок в Node.js
Данная статья является переводом. Ссылка на оригинал.

В статье рассмотрим:

  1. Объект Error
  2. Try…catch
  3. Throw
  4. Call stack
  5. Наименование функций
  6. Парадигму асинхронного программирования Promise

Значения имени ошибки

В свойстве “имя ошибки” может быть возвращено шесть различных значений:

ОшибкиОписание
EvalErrorAn error occurred in the eval () function
RangeErrorЧисло “вне диапазона” произошло
ReferenceErrorПроизошла незаконная ссылка
SyntaxErrorПроизошла синтаксическая ошибка
TypeErrorПроизошла ошибка типа
URIErrorПроизошла ошибка в encodeURI ()

Шесть различных значений описаны ниже.

Tasks

try…catch

Обработка ошибок в JavaScript осуществляется с помощью try…catch.

try…catch – это специальный синтаксис, состоящий из 2 блоков кода:

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

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

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

Код, приведённый выше мы обернули в try…catch, а именно ту его часть, в котором может возникнуть ошибка:

const text = ‘{name:”Александр”}’;
try {
const person = JSON.parse(text); // Uncaught SyntaxError: Unexpected token n in JSON at position 1
} catch(error) {
console.error(error);
console.log(error.message);
}
console.log(‘Это сообщение мы увидим!’);Обработка ошибок в JavaScript

Здесь в блоке try произойдет ошибка, так как в данном примере мы специально присвоили переменной text некорректную строку JSON. В catch эта ошибка будет присвоена параметру error, и в нём мы будем просто выводить эту ошибку в консоль с помощью console.error(). Таким образом она будет выведена также красным цветом, но без слова Uncaught, т.к. эта ошибка была поймана.

Ошибка – это объект и у него имеются следующие свойства:

  • message – описание ошибки;
  • name – тип ошибки, например, RangeError при указании значения выходящего за пределы диапазона;
  • stack – строка стека, которая используется в целях отладки; она позволяет узнать о том, что происходило в скрипте на момент возникновения ошибки.

В этом примере мы также написали инструкцию для вывода описание ошибки error.message в консоль с помощью console.log().

Пример функции для проверки корректности JSON:

const isValidJSON = (text) => {
try {
JSON.parse(text);
return true;
} catch {
return false;
}
}Функция для проверки корректности JSON, написанная с использование инструкции try...catch

При вызове функции, сначала будет выполняться инструкция JSON.parse(text). Если ошибки не возникнет, то возвратится значение true. В противном случае, интерпретатор перейдёт в секцию catch. В итоге будет возвращено false. Кстати здесь catch записан без указания круглых скобок и параметра внутри них. Эта возможность была добавлена в язык, начиная с версии ECMAScript 2019.

Зачем беспокоиться об обработке ошибок в Node.js?

Представьте, как разрабатываете RESTful web API на Node.js.

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

Секция finally

Конструкция try..catch может содержать ещё один блок: finally.

Выглядит этот расширенный синтаксис так:

try {
.. пробуем выполнить код ..
} catch(e) {
.. перехватываем исключение ..
} finally {
.. выполняем всегда ..
}

Секция finally не обязательна, но если она есть, то она выполняется всегда:

  • после блока try, если ошибок не было,
  • после catch, если они были.

Попробуйте запустить такой код?

try {
alert( ‘try’ );
if (confirm(‘Сгенерировать ошибку?’)) BAD_CODE();
} catch (e) {
alert( ‘catch’ );
} finally {
alert( ‘finally’ );
}

У него два варианта работы:

  1. Если вы ответите на вопрос «сгенерировать ошибку?» утвердительно, то try -> catch -> finally.
  2. Если ответите отрицательно, то try -> finally.

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

Например, мы хотим подсчитать время на выполнение функции sum(n), которая должна возвратить сумму чисел от 1 до n и работает рекурсивно:

function sum(n) {
return n ? (n + sum(n – 1)) : 0;
}

var n = +prompt(‘Введите n?’, 100);

var start = new Date();

try {
var result = sum(n);
} catch (e) {
result = 0;
} finally {
var diff = new Date() – start;
}

alert( result ? result : ‘была ошибка’ );
alert( “Выполнение заняло ” + diff );

Здесь секция finally гарантирует, что время будет подсчитано в любых ситуациях: при ошибке в sum или без неё.

Вы можете проверить это, запустив код с указанием n=100 – будет без ошибки, finally выполнится после try, а затем с n=100000 – будет ошибка из-за слишком глубокой рекурсии, управление прыгнет в finally после catch.

finally и return

Блок finally срабатывает при любом выходе из try..catch, в том числе и return.

В примере ниже из try происходит return, но finally получает управление до того, как контроль возвращается во внешний код.

function func() {

try {
// сразу вернуть значение
return 1;

} catch (e) {
/* … */
} finally {
alert( ‘finally’ );
}
}

alert( func() ); // сначала finally, потом 1

Если внутри try были начаты какие-то процессы, которые нужно завершить по окончании работы, то в finally это обязательно будет сделано.

Кстати, для таких случаев иногда используют try..finally вообще без catch:

function func() {
try {
return 1;
} finally {
alert( ‘Вызов завершён’ );
}
}

alert( func() ); // сначала finally, потом 1

В примере выше try..finally вообще не обрабатывает ошибки. Задача в другом: выполнить код при любом выходе из try – с ошибкой ли, без ошибок или через return.

Итого

Обработка ошибок – большая и важная тема.

В JavaScript для этого предусмотрены:

  • Конструкция try..catch..finally – она позволяет обработать произвольные ошибки в блоке кода.

    Это удобно в тех случаях, когда проще сделать действие и потом разбираться с результатом, чем долго и нудно проверять, не упадёт ли чего.

    Кроме того, иногда проверить просто невозможно, например JSON.parse(str) не позволяет «проверить» формат строки перед разбором. В этом случае блок try..catch необходим.

    Полный вид конструкции:

    try {
    .. пробуем выполнить код ..
    } catch(e) {
    .. перехватываем исключение ..
    } finally {
    .. выполняем всегда ..
    }

    Возможны также варианты try..catch или try..finally.

  • Оператор throw err генерирует свою ошибку, в качестве err рекомендуется использовать объекты, совместимые с встроенным типом Error, содержащие свойства message и name.

Кроме того, мы рассмотрели некоторые важные приёмы:

  • Проброс исключения – catch(err) должен обрабатывать только те ошибки, которые мы рассчитываем в нём увидеть, остальные – пробрасывать дальше через throw err.

    Определить, нужная ли это ошибка, можно, например, по свойству name.

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

  • В window.onerror можно присвоить функцию, которая выполнится при любой «выпавшей» из скрипта ошибке. Как правило, это используют в информационных целях, например отправляют информацию об ошибке на специальный сервис.

Анатомия Объекта Error

Первое, с чего стоит начать изучение – это объект Error.

Разберем на примере:

throw new Error(‘database failed to connect’);

Здесь происходят две вещи: создается объект Error и выбрасывается исключение.

Начнем с рассмотрения объекта Error, и того, как он работает. К ключевому слову throwвернемся чуть позже.

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

Встроенный конструктор ошибок – это общепринятый метод создания объектов ошибок.

Тем не менее, что же такое объекты ошибок? Почему они должны быть однородными? Это важные вопросы, поэтому давайте перейдем к ним.

Первым аргументом для объектаError является его описание.

Описание – это понятная человеку строка объекта ошибки. Также эта строка появляется в консоли, когда что-то пошло не так.

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

class FancyError extends Error {
constructor(args){
super(args);
this.name = “FancyError”
}
}

console.log(new Error(‘A standard error’))
// { [Error: A standard error] }

console.log(new FancyError(‘An augmented error’))
// { [Your fancy error: An augmented error] name: ‘FancyError’ }

Обработка ошибок становится проще, когда у нас есть согласованность в объектах.

Ранее мы упоминали, что хотим, чтобы объекты ошибок были однородными. Это поможет обеспечить согласованность в объекте ошибки.

Теперь давайте поговорим о следующей части головоломки – throw.

Ключевое слово Throw

Создание объектов ошибок – это не конец истории, а только подготовка ошибки к отправке. Отправка ошибки заключается в том, чтобы выбросить исключение. Но что значит выбросить? И что это значит для нашей программы?

Throw делает две вещи: останавливает выполнение программы и находит зацепку, которая мешает выполнению программы.

Давайте рассмотрим эти идеи одну за другой:

  • Когда JavaScript находит ключевое слово throw, первое, что он делает – предотвращает запуск любых других функций. Остановка снижает риск возникновения любых дальнейших ошибок и облегчает отладку программ.
  • Когда программа остановлена, JavaScript начнет отслеживать последовательную цепочку функций, которые были вызваны для достижения оператора catch. Такая цепочка называется стек вызовов(англ. call stack). Ближайший catch, который находит JavaScript, является местом, где возникает выброшенное исключение. Если операторы try/catch не найдены, тогда возникает исключение, и процесс Node.js завершиться, что приведет к перезапуску сервера.

Больше полезных материалов вы найдете на нашем телеграм-канале «Библиотека фронтендера»
Интересно, перейти к каналу

5- Выбросить Error

ECMAScript позволяет вам выбросить (throw) нечто во время работы программы, программа будет считать что произошла ошибка.
throw-any-example.js
console.log(” ——– Test throw any object ————“);
try {
let myObj = {};
throw myObj;
} catch(e) {
console.log(“Catch error: “);
console.log(e);
}
console.log(” ——– Test throw a Symbol ————“);
try {
let mySymbol = Symbol();
throw mySymbol;
} catch(e) {
console.log(“Catch error: “);
console.log(e);
}
console.log(” ——– Test throw a Number ————“);
try {
let myNumber = 100;
throw myNumber;
} catch(e) {
console.log(“Catch error: “);
console.log(e);
}
console.log(” ——– Test throw a String ————“);
try {
let myString = “Some error”;
throw myString;
} catch(e) {
console.log(“Catched error: “);
console.log(e);
}

Output:

——– Test throw any object ————
Catch errorr:
{}
——– Test throw a Symbol ————
Catch error:
Symbol()
——– Test throw a Number ————
Catch error:
100
——– Test throw a String ————
Catched error:
Some error

Обычно вы используете класс Error чтобы создать объект ошибки. Другие классы как SyntaxError, InternalError, RangeError, ReferenceError, SyntaxError, TypeError, URIError так же можно использовать в подходящем контексте.

Объект ошибки создается через класс Error (или его подклассы), при выбрасывании (throw) он будет содержать важную информацию как файл из-за которого произошла ошибка, местоположение ошибки, и информацию помогающую вам найти ошибку.
throw-error-example.js
console.log(“Three”);

// Create an Error
let myError = new Error(“Something error!”);
console.log(“Two”);
// Throw it!
throw myError;
console.log(“One”);

В простом случае вы можете выбросить что угодно, не объект класса Error (Или его подклассы). Но при нахождении ошибок данного вида вы не сможете имет информация как файл из-за которого происходит ошибка, местоположение происхождения ошибки,…
throw-string-error-example.js
console.log(“Three”);
try {
console.log(“Two”);
// Throw a String!
throw “Some error!!”;
} catch(e) {
console.log(“typeof e = ” + (typeof e));
// Log the error
console.log(e); // Some error!!
}
console.log(“One”);

Output:

Three
Two
typeof e = string
Some error!!
One

В ECMAScript, каждый блок try имеет соответствующий блок cache. Но в блоке try могут произойти разные виды ошибок, в данном случае вам нужно проверить ошибку найденную в блоке catch чтобы предоставить подходящую обработку для каждого вида ошибки.
catch-complex-example.js
let err = new Error(“My Error”);
let rangeErr = new RangeError();
let evalErr = new EvalError(“My Eval Error”);

// A random value in [0.. 9] let randomValue = Math.floor(Math.random() * 10);

// [0,1,2,3] let random0123 = randomValue % 4;
console.log(“random0123 = ” + random0123);
try {
if(random0123 == 0) {
throw err;
} else if(random0123 == 1){
throw rangeErr;
} else if(random0123 == 2) {
throw evalErr;
} else if(random0123 == 3) {
throw “A String Error”;
}
} catch(e) {
console.log(“typeof e = ” + (typeof e));// ‘object’ or ‘string’
if(e instanceof RangeError) {
console.log(“–> RangeError!!”);
} else if(e instanceof EvalError) {
console.log(“–> EvalError!!”);
} else if(e instanceof Error) {
console.log(“–> Error!!”);
} else if (typeof e == “string”){
console.log(“–> String Error!!”);
} else {
console.log(“–> Error!!”);
}
console.log(e);
}

Глобальная ловля ошибок

Возникновение ошибок, которые мы никак не обрабатываем с помощью try, можно очень просто перехватить посредством window.onerror:

window.onerror = function(message, source, lineno, colno, error) {
// …
}

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

  • message – строка, содержащее сообщение об ошибке;
  • source – URL-адрес скрипта или документа, в котором произошла ошибка;
  • lineno и colno – соответственно номер строки и столбца, в которой произошла ошибка;
  • error – объект ошибки или null, если соответствующий объект ошибки недоступен;

Using “try…catch”

Let’s explore a real-life use case of try…catch.

As we already know, JavaScript supports the JSON.parse(str) method to read JSON-encoded values.

Usually it’s used to decode data received over the network, from the server or another source.

We receive it and call JSON.parse like this:

let json = ‘{“name”:”John”, “age”: 30}’; // data from the server

let user = JSON.parse(json); // convert the text representation to JS object

// now user is an object with properties from the string
alert( user.name ); // John
alert( user.age ); // 30

You can find more detailed information about JSON in the JSON methods, toJSON chapter.

If json is malformed, JSON.parse generates an error, so the script “dies”.

Should we be satisfied with that? Of course not!

This way, if something’s wrong with the data, the visitor will never know that (unless they open the developer console). And people really don’t like when something “just dies” without any error message.

Let’s use try…catch to handle the error:

let json = “{ bad json }”;

try {

let user = JSON.parse(json); // <-- when an error occurs... alert( user.name ); // doesn't work} catch (err) { // ...the execution jumps here alert( "Our apologies, the data has errors, we'll try to request it one more time." ); alert( err.name ); alert( err.message ); }

Here we use the catch block only to show the message, but we can do much more: send a new network request, suggest an alternative to the visitor, send information about the error to a logging facility, … . All much better than just dying.

Бросаем исключения на примере

Мы рассмотрели теорию, а теперь давайте изучим пример:

function doAthing() {
byDoingSomethingElse();
}

function byDoingSomethingElse() {
throw new Error(‘Uh oh!’);
}

function init() {
try {
doAthing();
} catch(e) {
console.log(e);
// [Error: Uh oh!] }
}

init();

Здесь в функции инициализации init() предусмотрена обработка ошибок, поскольку она содержит try/catch блок.

init() вызывает функцию doAthing(), которая вызывает функцию byDoingSomethingElse(), где выбрасывается исключение. Именно в этот момент ошибки, программа останавливается и начинает отслеживать функцию, вызвавшую ошибку. Далее в функции init() и выполняет операторcatch. С помощью оператора catch мы решаем что делать: подавить ошибку или даже выдать другую ошибку (для распространения вверх).

Стек вызовов

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

Но как работает стек вызовов?

Всякий раз, когда вызывается функция, она помещается в стек, а при завершении удаляется из стека. Именно от этого стека мы получили название «трассировки стека».

Трассировка стека – это список функций, которые были вызваны до момента, когда в программе произошло исключение.

Она часто выглядит так:

Error: Uh oh!
at byDoingSomethingElse (/filesystem/aProgram.js:7:11)
at doAthing (/filesystem/aProgram.js:3:5)
at init (/filesystem/aProgram.js:12:9)
at Object. (/filesystem/aProgram.js:19:1)
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)

На этом этапе вам может быть интересно, как стек вызовов помогает нам с обработкой ошибок Node.js. Давайте поговорим о важности стеков вызовов.

Стек вызовов предоставляет «хлебные крошки», помогая проследить путь, который привел к исключению(ошибке).

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

Обратите внимание, что присвоить имена функциям в JavaScript не так просто. Итак, давайте кратко рассмотрим различные способы определения функций, и рассмотрим некоторые ловушки в именовании функций.

7- Перевыбросить ошибку (Re-throw Error)

При обработке исключения вы можете схватить это исключение и обработать или перевыбросить (rethrow) его наружу.
rethrow-example.js
function checkScore(score) {
if (score < 0 || score > 100) {
throw “Invalid Score ” + score;
}
}
function checkPlayer(name, score) {
try {
checkScore(score)
} catch (e) {
// Сделать что-то с exception
console.log(“Something invalid with player: ” + name + ” >> ” + e);
// Перевыбросить наружу.
throw e;
}
console.log(“OK Player ” + name + ” has score: ” + score );
}
// ————— TEST ————–
checkPlayer(“Tom”, 90);
checkPlayer(“Jerry”, -10);

Например, схватить исключение и перевыбросить (rethrow) другое исключение.
rethrow-example2.js
function checkScore(score) {
if (score < 0 || score > 100) {
throw “Invalid Score ” + score;
}
}
function checkPlayer(name, score) {
try {
checkScore(score)
} catch (e) {
// Сделать что-то с exception
console.log(“Something invalid with player: ” + name + ” >> ” + e);
// Потом выбросить другое исключение
throw (“Score ” + score +” invalid for player ” + name);
}
console.log(“OK Player ” + name + ” has score: ” + score );
}
// ————— TEST ————–
checkPlayer(“Tom”, 90);
checkPlayer(“Jerry”, -10);

Как называть функции

Чтобы понять, как называть функции, давайте рассмотрим несколько примеров:

// анонимная функция
const one = () => {};

// анонимная функция
const two = function () {};

// функция с явным названием
const three = function explicitFunction() {};

Вот три примера функций.

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

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

Во втором примере мы получили функциональное выражение. Это очень похоже на первый пример. Это анонимная функция, но просто объявленная с помощью ключевого словаfunction вместо синтаксиса жирной стрелки.

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

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

Обработка асинхронных исключений

Мы познакомились с объектом ошибок, ключевым словомthrow, стеком вызовов и наименованием функций. Итак, давайте обратим наше внимание на любопытный случай обработки асинхронных ошибок. Почему? Потому что асинхронный код ведет себя не так, как ожидаем. Асинхронное программирование необходимо каждому программисту на Node.js.

Javascript – это однопоточный язык программирования, а это значит, что Javascript запускается с использованием одного процессора. Из этого следует, что у нас есть блокирующий и неблокирующий код. Блокирующий код относится к тому, будет ли ваша программа ожидать завершения асинхронной задачи, прежде чем делать что-либо еще. В то время как неблокирующий код относится к тому, где вы регистрируете обратный вызов (callback) для выполнения после завершения задачи.

Стоит упомянуть, что есть два основных способа обработки асинхронности в JavaScript: promises (обещания или промисы) и callback (функция обратного вызова). Мы намеренно игнорируем async/wait, чтобы избежать путаницы, потому что это просто сахар поверх промисов.

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

ПримечаниеСуществует множество способов конвертировать код на основе callback-ов в promises. Например, вы можете использовать такую утилиту, как promisify, или обернуть свои обратные вызовы в промисы, например, так:var request = require(‘request’); //http wrapped module
function requestWrapper(url, callback) {
request.get(url, function (err, response) {
if (err) {
callback(err);
} else {
callback(null, response);
}
})
}

Мы разберемся с этой ошибкой, обещаю!

Давайте взглянем на анатомию обещаний.

Промисы в JavaScript – это объект, представляющий будущее значение. Promise API позволяют нам моделировать асинхронный код так же, как и синхронный. Также стоит отметить, что обещание обычно идет в цепочке, где выполняется одно действие, затем другое и так далее.

Но что все это значит для обработки ошибок Node.js?

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

Изучим код ниже:

function getData() {
return Promise.resolve(‘Do some stuff’);
}

function changeDataFormat() {
// …
}

function storeData(){
// …
}

getData()
.then(changeDataFormat)
.then(storeData)
.catch((e) => {
// Handle the error!
})

Здесь видно, как объединить обработку ошибок для трех различных функций в один обработчик, т. е. код ведет себя так же, как если бы три функции заключались в синхронный блок try/catch.

Окончательное заявление

Оператор finally позволяет выполнять код после try и catch независимо от результата:

try {
    Блок кода, чтобы попробовать
}
catch(err) {
    Блок кода для обработки ошибок
}

finally {
    Блок кода, который будет выполняться независимо от результата try/catch
}

Пример

function myFunction() {
    var message, x;
    message =
document.getElementById(“p01”);
    message.innerHTML = “”;
    x =
document.getElementById(“demo”).value;
   
try {
       
if(x == “”) throw “is empty”;
        if(isNaN(x))
throw “is not a number”;
       
x = Number(x);
        if(x>
10) throw “is too high”;
        if(x < 5) throw "is too low";
    }
    catch(err)
{
        message.innerHTML = “Error: ” +

8- Исключения при схватке ошибок

В ECMAScript имеются ситуации, когда вы думаете возникнет ошибка, но это не происходит. Например деление на 0 не создает ошибку, результат возвращает Infinity или -Infinity.
ex-Infinity-example.js
console.log( typeof Infinity ); // number

let a = 1 / 0;
console.log(a); // Infinity

let b = -1 / 0;
console.log(b); // -Infinity

Возьмите значение не число, чтобы поделить на число, результатом будет NaN (Not a Number).
ex-NaN-example.js
console.log( typeof NaN ); // number
console.log( isNaN(1) ); // false
console.log( isNaN( NaN ) ); // true

let a = “A String” / 2;
console.log(a); // NaN

let obj = {};
let b = obj / 2;
console.log(b); // NaN

Вы можете получить доступ в элемент с индексом любого массива  (Array) не вызывая ошибку.
ex-array-index-example.js
let myArray = [1, 100, 20];

console.log( myArray[1]); // 100
console.log( myArray[10] ); // undefined
console.log( myArray[-10] ); // undefined

View more Tutorials:

  • Pуководства ECMAScript, Javascript

Maybe you are interested

Это онлайн курс вне вебсайта o7planning, который мы представляем, он включает бесплатные курсы или курсы со скидкой.



  • The Complete JavaScript Bootcamp


  • Getting really good at JavaScript and TypeScript


  • Essentials of JavaScript Practice Coding Exercises Tips


  • JavaScript For Beginners – Learn JavaScript From Scratch


  • Learn JavaScript From Scratch:Become Top Rated Web Developer


  • Javascript for Beginners


  • Learning ECMAScript 6: Moving to the New JavaScript


  • JavaScript Intro to learning JavaScript web programming


  • JavaScript Dynamic Quiz Application from Scratch JSON AJAX


  • Byte-Sized-Chunks: Dynamic Prototypes in Javascript


  • Learn ECMAScript 2015 – ES6 Best Course


  • Getting started with javascript and its core concepts


  • *
    *

    Master ECMAScript 2015 (ES6)



  • Learn JavaScript Fundamentals


  • Quick JavaScript Core learning Course JavaScript Essentials


  • HTML CSS JavaScript: Most popular ways to code HTML CSS JS


  • *
    *

    Start 3D GIS Web Development in JavaScript



  • Learning JavaScript Programming Tutorial. A Definitive Guide


  • 2D Game Development With HTML5 Canvas, JS – Tic Tac Toe Game


  • The Web Development Course: HTML5, CSS3, JavaScript


  • JavaScript in 55 Minutes


  • JavaScript for Beginning Web Developers


  • JavaScript in Action JavaScript Projects


  • The complete beginner JavaScript ES5, ES6 and JQuery Course


  • *
    *

    Introductory To JavaScript – Learn The Basics of JavaScript

Pуководства ECMAScript, Javascript

  1.  

    Введение в Javascript и ECMAScript

  2.  

    Быстрый старт с Javascript

  3.  

    Диалоговое окно Alert, Confirm, Prompt в Javascript

  4.  

    Быстрый запуск с ECMAScript

  5.  

    Переменные в ECMAScript

  6.  

    Битовые операции

  7.  

    ECMAScript Arrays

  8.  

    Циклы в ECMAScript

  9.  

    ECMAScript Functions

  10.  

    ECMAScript Number

  11.  

    ECMAScript Boolean

  12.  

    ECMAScript Strings

  13.  

    Заявление if/else в ECMAScript

  14.  

    Заявление Switch в ECMAScript

  15.  

    Обработка ошибок в ECMAScript

  16.  

    ECMAScript Date

  17.  

    ECMAScript Modules

  18.  

    Функция setTimeout и setInterval в ECMAScript

  19.  

    Javascript Form Validation

  20.  

    ECMAScript Web Cookie

  21.  

    ECMAScript void Keyword

  22.  

    Классы и объекты в ECMAScript

  23.  

    Техника симулирования класса и наследственности в ECMAScript

  24.  

    Наследование и полиморфизм в ECMAScript

  25.  

    Понимание Duck Typing в ECMAScript

  26.  

    ECMAScript Symbols

  27.  

    ECMAScript Set Collection

  28.  

    ECMAScript Map Collection

  29.  

    Понимание ECMAScript Iterable и Iterator

  30.  

    Руководство Регулярное выражение ECMAScript

  31.  

    Руководство ECMAScript Promise, Async Await

  32.  

    Javascript Window

  33.  

    Javascript Console

  34.  

    Javascript Screen

  35.  

    Javascript Navigator

  36.  

    Javascript Geolocation API

  37.  

    Javascript Location

  38.  

    Javascript History API

  39.  

    Javascript Statusbar

  40.  

    Javascript Locationbar

  41.  

    Javascript Scrollbars

  42.  

    Javascript Menubar

  43.  

    JavaScript JSON

  44.  

    Обработка событий в Javascript

  45.  

    Javascript MouseEvent

  46.  

    Javascript WheelEvent

  47.  

    Javascript KeyboardEvent

  48.  

    Javascript FocusEvent

  49.  

    Javascript InputEvent

  50.  

    Javascript ChangeEvent

  51.  

    Javascript DragEvent

  52.  

    Javascript HashChangeEvent

  53.  

    Javascript URL Encoding

  54.  

    Javascript FileReader

  55.  

    Javascript XMLHttpRequest

  56.  

    Javascript Fetch API

  57.  

    Разбор XML в Javascript с помощью DOMParser

  58.  

    Введение в Javascript HTML5 Canvas API

  59.  

    Выделение кода с помощью библиотеки Javascript SyntaxHighlighter

  60.  

    Pуководства HTML

  61.  

    Pуководства CSS

  62.  

    Pуководства Bootstrap

  63.  

    Pуководства Dart

  64.  

    Pуководства TypeScript

Последняя надежда: window.onerror

Допустим, ошибка произошла вне блока try..catch или выпала из try..catch наружу, во внешний код. Скрипт упал.

Можно ли как-то узнать о том, что произошло? Да, конечно.

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

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

Например:

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

Существуют даже специальные веб-сервисы, которые предоставляют скрипты для отлова и аналитики таких ошибок, например: https://errorception.com/ или https://www.muscula.com/.

Отлавливать или не отлавливать?

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

Запомните «Золотое правило» – каждый раз обрабатывать исключения в обещаниях.

Summary

The try…catch construct allows to handle runtime errors. It literally allows to “try” running the code and “catch” errors that may occur in it.

The syntax is:

try {
// run this code
} catch (err) {
// if an error happened, then jump here
// err is the error object
} finally {
// do in any case after try/catch
}

There may be no catch section or no finally, so shorter constructs try…catch and try…finally are also valid.

Error objects have following properties:

  • message – the human-readable error message.
  • name – the string with error name (error constructor name).
  • stack (non-standard, but well-supported) – the stack at the moment of error creation.

If an error object is not needed, we can omit it by using catch { instead of catch (err) {.

We can also generate our own errors using the throw operator. Technically, the argument of throw can be anything, but usually it’s an error object inheriting from the built-in Error class. More on extending errors in the next chapter.

Rethrowing is a very important pattern of error handling: a catch block usually expects and knows how to handle the particular error type, so it should rethrow errors it doesn’t know.

Even if we don’t have try…catch, most environments allow us to setup a “global” error handler to catch errors that “fall out”. In-browser, that’s window.onerror.

Вызов ошибок с помощью оператора 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.

Источники

  • https://proglib.io/p/luchshie-praktiki-obrabotki-oshibok-v-node-js-2022-09-21
  • https://html5css.ru/js/js_errors.php
  • https://itchief.ru/javascript/try-catch
  • https://learn.javascript.ru/exception
  • https://betacode.net/12235/ecmascript-error-handling
  • https://javascript.info/try-catch
  • https://artzolin.ru/javascript-theory/javascript-error-handling/
[свернуть]
Решите Вашу проблему!


×
Adblock
detector