Советы

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

All JavaScript and system errors raised by Node.js inherit from, or are
instances of, the standard JavaScript class and are guaranteed
to provide at least the properties available on that class.

Node.js supports several mechanisms for propagating and handling errors that
occur while an application is running. How these errors are reported and
handled depends entirely on the type of Error and the style of the API that is
called.

All JavaScript errors are handled as exceptions that immediately generate
and throw an error using the standard JavaScript throw mechanism. These
are handled using the try…catch construct provided by the
JavaScript language.

try { const m = 1; const n = m + z;
} catch (err) {

}

Any use of the JavaScript throw mechanism will raise an exception that
must be handled or the Node.js process will exit immediately.

With few exceptions, Synchronous APIs (any blocking method that does not
return a nor accept a callback function, such as
fs.readFileSync), will use throw to report errors.

Errors that occur within Asynchronous APIs may be reported in multiple ways:

  • Some asynchronous methods returns a , you should always take into
    account that it might be rejected. See —unhandled-rejections flag for
    how the process will react to an unhandled promise rejection. const fs = require('fs/promises'); (async () => { let data; try { data = await fs.readFile('a file that does not exist'); } catch (err) { console.error('There was an error reading the file!', err); return;
    }

    })();

  • Most asynchronous methods that accept a callback function will accept an
    Error object passed as the first argument to that function. If that first
    argument is not null and is an instance of Error, then an error occurred
    that should be handled. const fs = require('node:fs');
    fs.readFile('a file that does not exist', (err, data) => { if (err) { console.error('There was an error reading the file!', err); return;
    }

    });

  • When an asynchronous method is called on an object that is an
    EventEmitter, errors can be routed to that object's 'error' event.
    const net = require('node:net');
    const connection = net.connect('localhost'); connection.on('error', (err) => { console.error(err);
    }); connection.pipe(process.stdout);
  • A handful of typically asynchronous methods in the Node.js API may still
    use the throw mechanism to raise exceptions that must be handled using
    try…catch. There is no comprehensive list of such methods; please
    refer to the documentation of each method to determine the appropriate
    error handling mechanism required.

The use of the 'error' event mechanism is most common for stream-based
and event emitter-based APIs, which themselves represent a series of
asynchronous operations over time (as opposed to a single operation that may
pass or fail).

For all EventEmitter objects, if an 'error' event handler is not
provided, the error will be thrown, causing the Node.js process to report an
uncaught exception and crash unless either: a handler has been registered for
the 'uncaughtException' event, or the deprecated node:domain
module is used.

const EventEmitter = require('node:events');
const ee = new EventEmitter(); setImmediate(() => { ee.emit('error', new Error('This will crash'));
});

Errors generated in this way cannot be intercepted using try…catch as
they are thrown after the calling code has already exited.

Developers must refer to the documentation for each method to determine
exactly how errors raised by those methods are propagated.

A generic JavaScript object that does not denote any specific
circumstance of why the error occurred. Error objects capture a «stack trace»
detailing the point in the code at which the Error was instantiated, and may
provide a text description of the error.

All errors generated by Node.js, including all system and JavaScript errors,
will either be instances of, or inherit from, the Error class.

new Error(message[, options])#

Creates a new Error object and sets the error.message property to the
provided text message. If an object is passed as message, the text message
is generated by calling String(message). If the cause option is provided,
it is assigned to the error.cause property. The error.

stack property will
represent the point in the code at which new Error() was called. Stack traces
are dependent on V8's stack trace API. Stack traces extend only to either
(a) the beginning of synchronous code execution, or (b) the number of frames
given by the property Error.

stackTraceLimit, whichever is smaller.

Error.captureStackTrace(targetObject[, constructorOpt])#

  • targetObject
  • constructorOpt

Creates a .stack property on targetObject, which when accessed returns
a string representing the location in the code at which
Error.captureStackTrace() was called.

const myObject = {};
Error.captureStackTrace(myObject);
myObject.stack;

The first line of the trace will be prefixed with
${myObject.name}: ${myObject.message}.

The optional constructorOpt argument accepts a function. If given, all frames
above constructorOpt, including constructorOpt, will be omitted from the
generated stack trace.

The constructorOpt argument is useful for hiding implementation
details of error generation from the user. For instance:

function a() { b();
} function b() { c();
} function c() { const { stackTraceLimit } = Error; Error.stackTraceLimit = 0; const error = new Error(); Error.stackTraceLimit = stackTraceLimit; Error.captureStackTrace(error, b); throw error;
} a();

Error.stackTraceLimit#

The Error.stackTraceLimit property specifies the number of stack frames
collected by a stack trace (whether generated by new Error().stack or
Error.captureStackTrace(obj)).

The default value is 10 but may be set to any valid JavaScript number. Changes
will affect any stack trace captured after the value has been changed.

If set to a non-number value, or set to a negative number, stack traces will
not capture any frames.

error.cause#

If present, the error.cause property is the underlying cause of the Error.
It is used when catching an error and throwing a new one with a different
message or code in order to still have access to the original error.

The error.cause property is typically set by calling
new Error(message, { cause }). It is not set by the constructor if the
cause option is not provided.

This property allows errors to be chained. When serializing Error objects,
util.inspect() recursively serializes error.cause if it is set.

const cause = new Error('The remote HTTP server responded with a 500 status');
const symptom = new Error('The message failed to send', { cause }); console.log(symptom);

error.code#

The error.code property is a string label that identifies the kind of error.
error.code is the most stable way to identify an error. It will only change
between major versions of Node.js. In contrast, error.message strings may
change between any versions of Node.js. See Node.js error codes for details
about specific codes.

error.message#

The error.message property is the string description of the error as set by
calling new Error(message).

The message passed to the constructor will also
appear in the first line of the stack trace of the Error, however changing
this property after the Error object is created may not change the first
line of the stack trace (for example, when error.stack is read before this
property is changed).

const err = new Error('The message');
console.error(err.message);

error.stack#

The error.stack property is a string describing the point in the code at which
the Error was instantiated.

Error: Things keep happening!
at /home/gbusey/file.js:525:2
at Frobnicator.refrobulate (/home/gbusey/business-logic.js:424:21)
at Actor. (/home/gbusey/actors.js:400:8)
at increaseSynergy (/home/gbusey/actors.js:701:6)

The first line is formatted as : , and
is followed by a series of stack frames (each line beginning with «at «).
Each frame describes a call site within the code that lead to the error being
generated.

V8 attempts to display a name for each function (by variable name,
function name, or object method name), but occasionally it will not be able to
find a suitable name. If V8 cannot determine a name for the function, only
location information will be displayed for that frame.

Otherwise, the
determined function name will be displayed with location information appended
in parentheses.

Frames are only generated for JavaScript functions. If, for example, execution
synchronously passes through a C++ addon function called cheetahify which
itself calls a JavaScript function, the frame representing the cheetahify call
will not be present in the stack traces:

const cheetahify = require('./native-binding.node'); function makeFaster() { cheetahify(function speedy() { throw new Error('oh no!'); });
} makeFaster();

The location information will be one of:

  • native, if the frame represents a call internal to V8 (as in [].forEach).
  • plain-filename.js:line:column, if the frame represents a call internal
    to Node.js.
  • /absolute/path/to/file.js:line:column, if the frame represents a call in
    a user program (using CommonJS module system), or its dependencies.
  • :///url/to/module/file.mjs:line:column, if the frame
    represents a call in a user program (using ES module system), or
    its dependencies.

The string representing the stack trace is lazily generated when the
error.stack property is accessed.

The number of frames captured by the stack trace is bounded by the smaller of
Error.stackTraceLimit or the number of available frames on the current event
loop tick.

ABORT_ERR#

Used when an operation has been aborted (typically using an AbortController).

APIs not using AbortSignals typically do not raise an error with this code.

This code does not use the regular ERR_* convention Node.js errors use in
order to be compatible with the web platform's AbortError.

ERR_ACCESS_DENIED#

A special type of error that is triggered whenever Node.js tries to get access
to a resource restricted by the Permission Model.

ERR_AMBIGUOUS_ARGUMENT#

A function argument is being used in a way that suggests that the function
signature may be misunderstood. This is thrown by the node:assert module when
the message parameter in assert.

throws(block, message) matches the error
message thrown by block because that usage suggests that the user believes
message is the expected message rather than the message the AssertionError
will display if block does not throw.

ERR_ARG_NOT_ITERABLE#

An iterable argument (i.e. a value that works with for…of loops) was
required, but not provided to a Node.js API.

ERR_ASSERTION#

A special type of error that can be triggered whenever Node.js detects an
exceptional logic violation that should never occur. These are raised typically
by the node:assert module.

ERR_ASYNC_CALLBACK#

An attempt was made to register something that is not a function as an
AsyncHooks callback.

ERR_ASYNC_TYPE#

The type of an asynchronous resource was invalid. Users are also able
to define their own types if using the public embedder API.

ERR_BROTLI_COMPRESSION_FAILED#

Data passed to a Brotli stream was not successfully compressed.

ERR_BROTLI_INVALID_PARAM#

An invalid parameter key was passed during construction of a Brotli stream.

ERR_BUFFER_CONTEXT_NOT_AVAILABLE#

An attempt was made to create a Node.js Buffer instance from addon or embedder
code, while in a JS engine Context that is not associated with a Node.js
instance. The data passed to the Buffer method will have been released
by the time the method returns.

When encountering this error, a possible alternative to creating a Buffer
instance is to create a normal Uint8Array, which only differs in the
prototype of the resulting object. Uint8Arrays are generally accepted in all
Node.js core APIs where Buffers are; they are available in all Contexts.

ERR_BUFFER_OUT_OF_BOUNDS#

An operation outside the bounds of a Buffer was attempted.

ERR_BUFFER_TOO_LARGE#

An attempt has been made to create a Buffer larger than the maximum allowed
size.

ERR_CANNOT_WATCH_SIGINT#

Node.js was unable to watch for the SIGINT signal.

ERR_CHILD_CLOSED_BEFORE_REPLY#

A child process was closed before the parent received a reply.

ERR_CHILD_PROCESS_IPC_REQUIRED#

Used when a child process is being forked without specifying an IPC channel.

ERR_CHILD_PROCESS_STDIO_MAXBUFFER#

Used when the main process is trying to read data from the child process's
STDERR/STDOUT, and the data's length is longer than the maxBuffer option.

ERR_CLOSED_MESSAGE_PORT#

There was an attempt to use a MessagePort instance in a closed
state, usually after .close() has been called.

ERR_CONSOLE_WRITABLE_STREAM#

Console was instantiated without stdout stream, or Console has a
non-writable stdout or stderr stream.

ERR_CONSTRUCT_CALL_INVALID#

A class constructor was called that is not callable.

ERR_CONSTRUCT_CALL_REQUIRED#

A constructor for a class was called without new.

ERR_CONTEXT_NOT_INITIALIZED#

The vm context passed into the API is not yet initialized. This could happen
when an error occurs (and is caught) during the creation of the
context, for example, when the allocation fails or the maximum call stack
size is reached when the context is created.

ERR_CRYPTO_CUSTOM_ENGINE_NOT_SUPPORTED#

Обработка исключений(Ошибок) Node JS+Express JS

Обработка исключений (ошибок) в Node JS + Express JS

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

Node JS — это серверная платформа, построенная на Chrome’s V8 JavaScript Engine, которая позволяет выполнять JavaScript код на стороне сервера. Использование Node JS вместе с фреймворком Express JS позволяет разрабатывать масштабируемые и высокопроизводительные веб-приложения.

В этой статье рассматриваем обработку исключений в Node JS + Express JS.

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

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

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

После того, как Node JS обнаруживает исключение, он генерирует событие uncaughtException. Для обработки ошибок в Node JS разработчики могут реализовывать обработчики исключений (ошибок). Обработчик исключения — это функция, которая вызывается при возникновении исключения.

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

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

Express JS — это минималистичный и гибкий фреймворк для создания веб-приложений на Node JS. В Express JS обработка ошибок осуществляется с помощью промежуточного ПО (Middleware).

  • Middleware — это функция, которая вызывается перед обработкой главного запроса и может обрабатывать или изменять входные данные запроса.
  • Для обработки ошибок в Express JS можно использовать функцию middleware, которая обрабатывает возникшие ошибки и выводит пользователю понятную информацию.
  • Пример обработки ошибок в Express JS:

app.get('/', function(req, res, next) {
fs.readFile('file-does-not-exist', function(err, data) {
if (err) {
next(err); // передача ошибки в обработчик исключений
} else {
res.send(data);
}
});
});

app.use(function(err, req, res, next) {
console.error(err.stack);
res.status(500).send('Something broke!');
});

В данном примере мы обращаемся к несуществующему файлу, и при ошибке передаем ее в обработчик исключений. Функция обработчика исключений выводит сообщение об ошибке на странице с кодом 500.

Также в Express JS имеется возможность настройки обработчика исключений для каждого запроса.

Пример:

app.get('/', function(req, res, next) {
fs.readFile('file-does-not-exist', function(err, data) {
if (err) {
res.status(500); // установка статуса ошибки
res.render('error', { error: err.message }); // передача сообщения об ошибке в шаблон
} else {
res.send(data);
}
});
});

app.use(function(req, res, next) {
res.status(404); // установка статуса ошибки 404
res.render('404', { url: req.originalUrl }); // передача URL в шаблон
});

В данном примере мы обращаемся к несуществующему файлу, и при ошибке передаем сообщение об ошибке в шаблон с кодом 500. Если при обработке запроса происходит ошибка 404, то мы передаем URL в шаблон с кодом 404.

  1. Кроме middleware, в Express JS имеются другие инструменты для обработки исключений и управления ошибками, например, библиотека express-error-handler.
  2. С помощью express-error-handler мы можем настроить страницы для отображения исключений и перенаправлять пользователей на нужную страницу при возникновении ошибок.
  3. Итог

Обработка исключений очень важна при разработке веб-приложений и серверных приложений на Node JS. Неверная обработка ошибок может привести к нежелательным последствиям.

Node JS предоставляет возможности для обработки исключений при помощи обработчиков исключений. В Express JS обработку исключений можно настроить с помощью middleware и других инструментов.

Надеюсь, эта статья поможет вам правильно обрабатывать ошибки при разработке веб-приложений на Node JS.

Handling Node.js Exceptions

Debugging errors is the hardest part of programming. Errors can appear in your code in a variety of ways, whether as syntax errors, errors in logic, or the most dreaded of all, runtime errors. Runtime errors occur whenever something unexpected occurs in your application, and they often lead to catastrophic issues that can crash your program.

Like many languages, Node.js provides a mechanism to anticipate errors before they occur. When an error occurs in your code, it turns into an object called an exception. Properly handling these exceptions allows you to recover gracefully from unforeseen issues, resulting in a much better user experience.

In this post, we'll take a look at what causes these errors in Node.js, and how to recover from them.

What can cause an error exception in Node.js?

Runtime errors can occur in a variety of ways. One example includes referencing an undefined variable, or passing the wrong type to an argument.

Other common Node.js errors include:

  1. EvalError: errors that occur within the global function eval
  2. RangeError: these errors occur when you attempt to access a variable outside its range, such as trying to get the fifth element of an array with only three items
  3. ReferenceError: these errors arise when you attempt to use a variable that doesn't exist
  4. SyntaxError: these errors come from invalid code
  5. TypeError: this occurs when you attempt to use a variable that is not a valid type
  6. URIError: this error occurs whenever encodeURI or decodeURI are given invalid parameters

In most cases, these exceptions occur outside of your control. For example, if you run the following line of code:

null.toString()

You should expect to receive the following error:

Uncaught TypeError: Cannot read property 'someMethod' of null

Node.js Throw Exception

However, you can also throw an error yourself:

throw new Error('Throw makes it go boom!')

Why would you want to do this? Well, consider the case where you know an error can occur. For example, suppose you have a calculator app written in Node.js, and you want to throw an error if the denominator in the division function is a zero:

function divide(numerator, denominator) {
if (denominator == 0) {
throw new Error('divide by zero!')
}
else {
return numerator / denominator;
}
}

Why would you want to do this? Well, admittedly, catching a generic Error is not very helpful. But you can create a custom error subclass:

class DivideByZeroError extends Error {
constructor(message) {
super(message);
this.name = «DivideByZeroError»;
}
}

This allows you to set up a more appropriate error in the code:

function divide(numerator, denominator) {
if (denominator == 0) {
throw new DivideByZeroError('divide by zero!')
}
else {
return numerator / denominator;
}
}

If you take this concept further, you could throw errors for any kind of issue. Compare the following ways of handling errors:

let user = JSON.parse(json);

if (!user.age || user.age < 0) { throw new InvalidAgeError("No field: age"); } if (!user.name) { throw new MissingNameError("No field: name"); } if (!user.address) { throw new Error("Required"); }

By throwing your own exception, your immediate benefit will be a more legible code base, as in the case of age and name above.

But your error classes will also show up in your logs and data analytics pipelines, which can help you narrow down specifically what problems may be occurring.

When trying to track down an error, it can be hard to rationalize what Error is supposed to mean, as opposed to InvalidAge or MissingName.

Regardless, catching errors like this is essential in Node.js. Because Node runs in an asynchronous environment, an event that occurs sometimes doesn't have an immediate effect. In our example above, we tried to rely on user.

address in a situation where it doesn't exist, however, the program will crash, but not when we might expect it to! On the other hand, try/catch operates synchronously; as soon as an error is thrown, the program halts, allowing you to recover without unintended side effects.

How do you protect your application against exceptions?

If you know where an error can occur, your next likely step would be to prevent this error from taking down your application or upsetting your user. To prevent this, Node.js has a special syntax called the try-catch block.

As its name suggests, a try-catch block lets you run a piece of code, and if an error is thrown, halts the program and lets you recover. Let's take a look at how that might work in practice with our earlier toString() example. Suppose you have a function like this:

function showString(var) {
return var.toString();
}

Now, we can't guarantee that toString() will work on var. Maybe it's null, or maybe this function is called on some dynamic user input we can't control. Since we know we have the potential to run into a TypeError here, we can use a try-catch block to anticipate this error and do something else:

function showString(var) {
try {
return var.toString()
} catch (TypeError) {
// report error message to the user
return «Error! String not given»;
}
}

If there is no error, then the code in the catch block is never executed. Keep in mind that the error provided to the catch statement must match the error you expect, or else that code won't run. Unfortunately, this can prevent your code from catching errors, and try-catch not working.

If you think an error might occur, but you aren't sure what its type would be, and you want to ensure that you catch any potential errors, you can just protect against the base Error class:

try {
someFunction();
}
catch (Error) {
// do something else
}

Since all errors extend this base Error class, it will catch any thrown errors.

Another neat addition is the ability to add a finally statement to these lines. For example:

try {
openDatabase();
updateDatabase(x, y);
}
catch (Error) {
return «Error! Database could not be updated.»;
}
finally {
// always a good idea to close a database connection when you're done
closeDatabase();
}

finally will always execute at the end of the try-catch block, regardless of whether or not the catch statement catches an error. It can clean up any remaining state, such as closing an open database connection. At least one catch or a finally must be present in a try block.

Wrapping Up

Exceptions and try-catch blocks aren't unique to Node. For a more high-level overview, you can check out their documentation on the subject.

We've talked a lot about exceptions in this article. It's important to keep in mind that errors can still occur in alarming volume, despite your best efforts to prevent them.

Mistaken code can always make it to production, and rather than foist availability issues onto your users, a platform like Rollbar can help you fix unforeseen issues before they become problems.

With its real-time Node error monitoring, AI-assisted workflows, and root cause analysis, Rollbar can protect your app by predicting problems before they affect your users.

Track, Analyze and Manage Errors With Rollbar

![Rollbar in action](https://rollbar.com/wp-content/uploads/2022/04/section-1-real-time-errors@2x-1-300×202.png)

Managing errors and exceptions in your code is challenging. It can make deploying production code an unnerving experience. Being able to track, analyze, and manage errors in real-time can help you to proceed with more confidence. Rollbar automates error monitoring and triaging, making fixing errors easier than ever. Try it today.

Работа с ошибками · Super book of Node.js

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

Начальные требования

Базовые знания Javascript и Node.js

Ошибки в Node.js

Приложение, работающее в Node.js в общем может сталкиватся с четырьмя категориями ошибок:

  • Стандартные ошибки JavaScript, такие как:
    • : возникает при неуспешном вызове eval().
    • : возникает в ответ на неправильный JavaScript синтаксис.
    • : возникает если значение выходит за ожидаемый диапазон.
    • : возникает при использовании неопределенных переменных.
    • : возникает при передаче аргументов неправильных типов.
    • : возникает если глобальная функция обработки URI используется неправильно.
  • Системные ошибки, вызываемые операционной системой — такие как попытка открыть файл, который не сущетсвует, попытка отправит данный на закрытый сокет и т. д.
  • Определенные пользователем ошибки, вызываемые кодом приложения.
  • Assertion Errors — специальный класс ошибок, которые могут возникнуть, если Node.js обнаруживает ошибочное нарушение логики, которое не должно произойти. Ошибки такого класса вызываеются обычно модулем assert.

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

Распространение и перехват ошибок

Node.js поддерживает несколько механизмов распространения и обработки ошибок, возникающих при работе приложения. То, как эти ошибки сообщаются и обрабатывается, зависит полностью от типа ошибки и от вызываемого API.

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

try { const m = 1; const n = m + z;
} catch (err) {

}

Любое использование JavaScript throwвызовет исключение, которое должно быть обработано с помощью try / catch, иначе процесс Node.js немедленно завершится.

За небольшим исключением синхронные API (любые блокирующие методы, которые не принимают колбэк, такие как fs.readFileSync) используют throw для сообщения об ошибке.

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

  • Большинство асинхронных методов, принимающих колбэк функцию, принимают также объект ошибки, передаваемый как первый параметр в эту функцию. Если этот перый аргумент не равен null, и является экземпляром Error, значит произошла ошибка, которая должна быть обработана.);

const fs = require('fs'); fs.readFile('a file that does not exist', (err, data) => { if (err) { console.error('There was an error reading the file!', err); return;
}

});

  • Если асинхронный метод вызван объектом, который является EventEmitter, ошибки могут быть перенаправлены в событие 'error' этого объекта.

const net = require('net');
const connection = net.connect('localhost'); connection.on('error', (err) => { console.error(err);
});

connection.pipe(process.stdout);

  • Небольшое количтество типично асинхронных методов в Node.js API могут использовать механизм throw
    для вызова исключений, которые должны быть обработаны с использованием try / catch. Не существует исчерпывающего списка этих методов, следует обращаться к документации каждого метода для определенифя подходящего механизма обработки ошибок.

Использование события 'error' является наиболее общим для stream-based и event emitter-based API, которые сами по себе представляю срию асинхронных операций во времени (в отличии от единственной операции, которая может пройти или упасть).

Для всех объектов типа EventEmitter, если обработчик события 'error' не указан, будет выброшена ошибка, что завставит процесс Node.js сообщить о необработанном исключении и прерваться, за исключением случаев, когда подключен модуль domain или зарегистрирован обработчик события типа process.on('uncaughtException').

const EventEmitter = require('events');
const ee = new EventEmitter(); setImmediate(() => { ee.emit('error', new Error('This will crash'));
});

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

Колбэки в стиле Node.js

Большинство асинхронных методов представленный в базовом API Node.js следуют простому шаблону, называемому «колбэк в стиле Node.js». По этому шаблону колбэк функция передается в метод в качестве аргумента.

Когда опреация завершается, или же возникает ошибка, вызывается эта функция, с объектом ошибки (при наличии), передаваемом как первый аргумент. Если ошибок не произошло, первый аргумент передается равным null.

const fs = require('fs'); function nodeStyleCallback(err, data) { if (err) { console.error('There was an error', err); return; } console.log(data);
} fs.readFile('/some/file/that/does-not-exist', nodeStyleCallback);
fs.readFile('/some/file/that/does-exist', nodeStyleCallback);

Механизм JavaScript try / catch не может быть использован для прехвата ошибок, генерируемых асинхронными методами API. Распространенная ошибка новичков — попытка использовать throw внутри колбека в стиле Node.js:

const fs = require('fs'); try { fs.readFile('/some/file/that/does-not-exist', (err, data) => { if (err) { throw err; } });
} catch (err) { console.error(err);
}

Это не будет работать, поскольку колбэк-функция передаваемая в fs.readFile() вызывается асинхронно. В то время, когда коолбек вызван, окружающий код (включая try { } catch (err) { }) уже окончил работу. Выбрасываение ошибки внутри колбэка может завалить процесс Node.js в большинстве случаев.

Класс Error

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

Все ошибки JavaScript и системные, вызываемые Node.js наследуются или являются экземплярами класса Error.+

Подробное описание свойств и методов класса Error, а также его подклассов, см. в документации.

Практическое задание

  1. Составить синхронный алгоритм, генерирующий ошибку и написать код, обрабатывающий её.
  2. Составить асинхронный алгоритм, генерирующий ошибку, и написать код, обрабатывающи её.
  3. Составить алгоритм с генерацией событий, генерирующий ошибку, и написать код, обрабатывающий её.

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

Функции промежуточного обработчика для обработки ошибок определяются так же, как и другие функции промежуточной обработки, но с указанием для функции обработки ошибок не трех, а четырех аргументов: (err, req, res, next). Например:

app.use(function(err, req, res, next) {
console.error(err.stack);
res.status(500).send('Something broke!');
});

Промежуточный обработчик для обработки ошибок должен быть определен последним, после указания всех app.use() и вызовов маршрутов; например:

var bodyParser = require('body-parser');
var methodOverride = require('method-override');

app.use(bodyParser());
app.use(methodOverride());
app.use(function(err, req, res, next) {
// logic
});

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

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

var bodyParser = require('body-parser');
var methodOverride = require('method-override');

app.use(bodyParser());
app.use(methodOverride());
app.use(logErrors);
app.use(clientErrorHandler);
app.use(errorHandler);

В данном примере базовый код logErrors может записывать информацию о запросах и ошибках в stderr, например:

function logErrors(err, req, res, next) {
console.error(err.stack);
next(err);
}

Кроме того, в данном примере clientErrorHandler определен, как указано ниже; в таком случае ошибка явным образом передается далее следующему обработчику:

function clientErrorHandler(err, req, res, next) {
if (req.xhr) {
res.status(500).send({ error: 'Something failed!' });
} else {
next(err);
}
}

“Обобщающая” функция errorHandler может быть реализована так:

function errorHandler(err, req, res, next) {
res.status(500);
res.render('error', { error: err });
}

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

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

app.get('/a_route_behind_paywall',
function checkIfPaidSubscriber(req, res, next) {
if(!req.user.hasPaid) {

// continue handling this request
next('route');
}
}, function getPaidContent(req, res, next) {
PaidContent.find(function(err, doc) {
if(err) return next(err);
res.json(doc);
});
});

В данном примере обработчик getPaidContent будет пропущен, но выполнение всех остальных обработчиков в app для /a_route_behind_paywall будет продолжено.

Стандартный обработчик ошибок

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

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

При вызове next() с ошибкой после начала записи ответа
(например, если ошибка обнаружена во время включения ответа в поток, направляемый клиенту), стандартный обработчик ошибок Express закрывает соединение и отклоняет запрос.

Поэтому при добавлении нестандартного обработчика ошибок вам потребуется делегирование в стандартные
механизмы обработки ошибок в Express в случае, если заголовки уже были отправлены клиенту:

function errorHandler(err, req, res, next) {
if (res.headersSent) {
return next(err);
}
res.status(500);
res.render('error', { error: err });
}

Порядок выполнения и обработка ошибок — JavaScript | MDN

  • « Предыдущая статья
  • Следующая статья »

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

Более подробная информация об инструкциях, рассмотренных в данной главе, содержится в справочнике по JavaScript. Точка с запятой (;) используется для разделения инструкций в коде.

Любое выражение (expression) в JavaScript является также инструкцией (statement). Чтобы получить более подробную информацию о выражениях, прочитайте Выражения и операторы.

Инструкция block является фундаментальной и используется для группировки других инструкций. Блок ограничивается фигурными скобками:

{ statement_1; statement_2; … statement_n; }

Блок обычно используется с управляющими инструкциями (например, if, for, while).

В вышеприведённом примере { x++; } является блоком.

Обратите внимание: в JavaScript отсутствует область видимости блока до ECMAScript2015.

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

«Автономные» (standalone) блоки в JavaScript могут продуцировать полностью отличающийся результат, от результата в языках C или Java. Например:

var x = 1;
{ var x = 2;
}
console.log(x);

В вышеприведённом примере инструкция var x внутри блока находится в той же области видимости, что и инструкция var x перед блоком. В C или Java эквивалентный код выведет значение 1.

Начиная с ECMAScript 6, оператор let позволяет объявить переменную в области видимости блока. Чтобы получить более подробную информацию, прочитайте let.

Условная инструкция — это набор команд, которые выполняются, если указанное условие является истинным. JavaScript поддерживает две условные инструкции: if…else и switch.

Используйте оператор if для выполнения инструкции, если логическое условия истинно. Используйте опциональный else, для выполнения инструкции, если условие ложно. Оператор if выглядит так:

if (condition) { statement_1;
} else { statement_2;
}

Здесь condition может быть любым выражением, вычисляемым как истинное (true) или ложное (false). Чтобы получить более подробную информацию о значениях true и false, прочитайте Boolean. Если условие оценивается как true, то выполняется statement_1, в противном случае — statement_2. Блоки statement_1 и statement_2 могут быть любыми блоками, включая также вложенные инструкции if.

Также вы можете объединить несколько инструкций, пользуясь else if для получения последовательности проверок условий:

if (condition_1) { statement_1;} else if (condition_2) { statement_2;} else if (condition_n) { statement_n; } else { statement_last;}

В случае нескольких условий только первое логическое условие, которое вычислится истинным (true), будет выполнено. Используйте блок ({ … }) для группировки нескольких инструкций. Применение блоков является хорошей практикой, особенно когда используются вложенные инструкции if:

if (condition) {
statement_1_runs_if_condition_is_true;
statement_2_runs_if_condition_is_true;
} else {
statement_3_runs_if_condition_is_false;
statement_4_runs_if_condition_is_false;
}

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

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

Ложные значения

Следующие значения являются ложными:

  • false
  • undefined
  • null
  • 0
  • NaN
  • пустая строка ( «» )

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

Не путайте примитивные логические значения true и false со значениями true и false объекта Boolean. Например:

var b = new Boolean(false);
if (b) if (b == true)

В следующем примере функция checkData возвращает true, если число символов в объекте Text равно трём; в противном случае функция отображает окно alert и возвращает false.

function checkData() { if (document.form1.threeChar.value.length == 3) { return true; } else { alert( «Enter exactly three characters. » + document.form1.threeChar.value + » is not valid.», ); return false; }
}

Инструкция switch позволяет сравнить значение выражения с различными вариантами и при совпадении выполнить соответствующий код. Инструкция имеет следующий вид:

switch (expression) {
case label_1:
statements_1
[break;]
case label_2:
statements_2
[break;]

default:
statements_default
[break;]
}

Сначала производится поиск ветви case с меткой label, совпадающей со значением выражения expression. Если совпадение найдено, то соответствующий данной ветви код выполняется до оператора break, который прекращает выполнение switch и передаёт управление дальше.

В противном случае управление передаётся необязательной ветви default и выполняется соответствующий ей код. Если ветвь default не найдена, то программа продолжит выполняться со строчки, следующей за инструкцией switch.

По соглашению ветвь default является последней ветвью, но следовать этому соглашению необязательно.

Если оператор break отсутствует, то после выполнения кода, который соответствует выбранной ветви, начнётся выполнение кода, который следует за ней.

В следующем примере если fruittype имеет значение «Bananas», то будет выведено сообщение «Bananas are $0.48 a pound.» и оператор break прекратит выполнение switch. Если бы оператор break отсутствовал, то был бы также выполнен код, соответствующий ветви «Cherries», т.е. выведено сообщение «Cherries are $3.00 a pound.».

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *