Ошибки
Приложения, работающие в Node.js, обычно сталкиваются с четырьмя категориями ошибок:
- Стандартные ошибки JavaScript, такие как:
-
<EvalError> : возникает при неудачном вызове
eval(). - <SyntaxError> : возникает из-за неправильного синтаксиса языка JavaScript.
- <RangeError> : возникает, когда значение не находится в ожидаемом диапазоне.
- <ReferenceError> : возникает при использовании неопределённых переменных.
- <TypeError> : возникает при передаче аргументов неверного типа.
- <URIError> : возникает при неправильном использовании глобальной функции обработки URI.
-
<EvalError> : возникает при неудачном вызове
- Системные ошибки, вызванные ограничениями операционной системы, такие как попытка открыть несуществующий файл или отправка данных по закрытому сокету;
- И пользовательские ошибки, возникающие из-за кода приложения.
- Ошибки утверждения — это особый класс ошибок, которые могут возникать, когда Node.js обнаруживает нарушение логики, которое никогда не должно происходить. Обычно они поднимаются модулем
assert.
Все ошибки JavaScript и системные ошибки, генерируемые Node.js, наследуются от или являются экземплярами стандартного класса JavaScript <Error> и гарантированно предоставляют по крайней мере свойства, доступные в этом классе.
Распространение и перехват ошибок
Node.js поддерживает несколько механизмов для распространения и обработки ошибок, возникающих во время выполнения приложения. Как эти ошибки сообщаются и обрабатываются, полностью зависит от типа ошибки и стиля вызываемого API.
Все ошибки JavaScript обрабатываются как исключения, которые немедленно генерируют и бросают ошибку с использованием стандартного механизма JavaScript throw. Они обрабатываются с помощью конструкции try / catch, предоставляемой языком JavaScript.
// Throws with a ReferenceError because z is undefined
try {
const m = 1;
const n = m + z;
} catch (err) {
// Handle the error here.
}
Любое использование механизма JavaScript throw вызовет исключение, которое обязательно нужно обработать с помощью try / catch, иначе процесс Node.js завершится немедленно.
За исключением немногих случаев, синхронные API (любой блокирующий метод, не принимающий функцию callback, например, fs.readFileSync) будут использовать throw для отчёта об ошибках.
Ошибки, возникающие в рамках асинхронных API, могут быть сообщены несколькими способами:
- Большинство асинхронных методов, принимающих функцию
callback, также примут объектError, переданный в качестве первого аргумента этой функции. Если этот первый аргумент не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;
}
// Otherwise handle the data
});
-
Когда асинхронный метод вызывается на объекте, являющемся
EventEmitter, ошибки могут быть перенаправлены на событие'error'этого объекта.const net = require('net'); const connection = net.connect('localhost'); // Adding an 'error' event handler to a stream: connection.on('error', (err) => { // If the connection is reset by the server, or if it can't // connect at all, or on any sort of error encountered by // the connection, the error will be sent here. console.error(err); }); connection.pipe(process.stdout); -
Несколько типично асинхронных методов API Node.js всё же могут использовать механизм
throwдля вызова исключений, которые нужно обработать с помощьюtry / catch. Нет исчерпывающего списка таких методов; обратитесь к документации каждого метода, чтобы определить требуемый механизм обработки ошибок.
Использование механизма событий 'error' наиболее распространено для API на основе потоков stream и генераторов событий EventEmitter, которые сами по себе представляют серию асинхронных операций во времени (в отличие от единственной операции, которая может пройти успешно или завершиться с ошибкой).
Для всех EventEmitter объектов, если обработчик события 'error' не предоставлен, ошибка будет выброшена, заставив процесс Node.js сообщить об необработанном исключении и аварийно завершиться, если не используется модуль domain должным образом или не зарегистрирован обработчик для события process.on('uncaughtException').
const EventEmitter = require('events');
const ee = new EventEmitter();
setImmediate(() => {
// This will crash the process because no 'error' event
// handler has been added.
ee.emit('error', new Error('This will crash'));
});
Ошибки, сгенерированные таким образом, не могут быть перехвачены с помощью try / catch, так как они выбрасываются после того, как вызывающий код уже завершил выполнение.
Разработчики должны обратиться к документации каждого метода, чтобы точно определить, как обрабатываются ошибки, генерируемые этими методами.
Обратные вызовы в стиле Node.js
Большинство асинхронных методов, экспортируемых ядром 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:
// THIS WILL NOT WORK:
const fs = require('fs');
try {
fs.readFile('/some/file/that/does-not-exist', (err, data) => {
// mistaken assumption: throwing here...
if (err) {
throw err;
}
});
} catch (err) {
// This will not catch the throw!
console.error(err);
}
Это не сработает, потому что функция обратного вызова, переданная в fs.readFile(), вызывается асинхронно. К моменту вызова обратного вызова окружающий код (включая блок try { } catch (err) { }) уже завершится. Выброс ошибки внутри обратного вызова может привести к аварийному завершению процесса Node.js в большинстве случаев. Если включены домены (domains), или зарегистрирован обработчик для process.on('uncaughtException'), такие ошибки можно перехватить.
Класс: Error
Объект JavaScript Error, который не обозначает каких-либо конкретных обстоятельств, почему произошла ошибка. Объекты Error захватывают «стек вызовов», отображающий точку в коде, в которой был создан Error, и могут предоставить текстовое описание ошибки.
Все ошибки, генерируемые Node.js, включая все системные и JavaScript-ошибки, либо являются экземплярами, либо наследуются от класса Error.
new Error(message)
-
message<строка>
Создаёт новый объект Error и устанавливает свойство error.message на предоставленное текстовое сообщение. Если в качестве message передаётся объект, текстовое сообщение генерируется путём вызова message.toString(). Свойство error.stack будет представлять точку в коде, в которой был вызван new Error(). Стеки вызовов зависят от API стека вызовов V8. Стеки вызовов распространяются только до (a) начала синхронного выполнения кода или (b) числа кадров, заданного свойством Error.stackTraceLimit, в зависимости от того, что меньше.
Error.captureStackTrace(targetObject[, constructorOpt])
Создаёт свойство .stack на targetObject, которое при обращении возвращает строку, представляющую местоположение в коде, где был вызван Error.captureStackTrace().
const myObject = {};
Error.captureStackTrace(myObject);
myObject.stack; // similar to `new Error().stack`
Первая строка стека, вместо того, чтобы быть префиксной со ErrorType:
message, будет результатом вызова targetObject.toString().
Необязательный аргумент constructorOpt принимает функцию. Если указана, все кадры выше constructorOpt, включая constructorOpt, будут исключены из сгенерированного стека вызовов.
Аргумент constructorOpt полезен для скрытия от конечного пользователя реализационных деталей генерации ошибок. Например:
function MyError() {
Error.captureStackTrace(this, MyError);
}
// Without passing MyError to captureStackTrace, the MyError
// frame would show up in the .stack property. By passing
// the constructor, we omit that frame, and retain all frames below it.
new MyError().stack;
Error.stackTraceLimit
Свойство Error.stackTraceLimit определяет количество кадров стека, собираемых стеком вызовов (сгенерированным new Error().stack или Error.captureStackTrace(obj)).
Значение по умолчанию — 10, но может быть установлено на любое допустимое числовое значение JavaScript. Изменения повлияют на любой стек вызовов, захваченный после изменения значения.
Если установлено значение, не являющееся числом, или установлено отрицательное значение, стеки вызовов не будут захватывать какие-либо кадры.
error.message
Свойство error.message — это строковое описание ошибки, установленное путём вызова new Error(message). Переданная в конструктор message также появится в первой строке стека вызовов объекта Error, однако изменение этого свойства после создания объекта Error может не изменить первую строку стека вызовов (например, когда error.stack читается до изменения этого свойства).
const err = new Error('The message');
console.error(err.message);
// Prints: The message
error.stack
Свойство error.stack — это строка, описывающая точку в коде, где был создан Error.
Например:
Error: Things keep happening! at /home/gbusey/file.js:525:2 at Frobnicator.refrobulate (/home/gbusey/business-logic.js:424:21) at Actor.<anonymous> (/home/gbusey/actors.js:400:8) at increaseSynergy (/home/gbusey/actors.js:701:6)
Первая строка отформатирована как <error class name>: <error message>, и за ней следуют ряд стековых кадров (каждая строка начинается с "at "). Каждый кадр описывает место вызова в коде, приведшее к возникновению ошибки. V8 пытается отобразить имя каждой функции (по имени переменной, имени функции или имени метода объекта), но иногда не может найти подходящее имя. Если V8 не может определить имя функции, для этого кадра будет отображена только информация о местоположении. В противном случае отобразится определенное имя функции с добавленной информацией о местоположении в скобках.
Важно отметить, что кадры генерируются только для JavaScript-функций. Например, если выполнение синхронно проходит через C++-дополнительную функцию, называемую cheetahify, которая сама вызывает JavaScript-функцию, кадр, представляющий вызов cheetahify, не будет присутствовать в стековом следе:
const cheetahify = require('./native-binding.node');
function makeFaster() {
// cheetahify *synchronously* calls speedy.
cheetahify(function speedy() {
throw new Error('oh no!');
});
}
makeFaster(); // will throw:
// /home/gbusey/file.js:6
// throw new Error('oh no!');
// ^
// Error: oh no!
// at speedy (/home/gbusey/file.js:6:11)
// at makeFaster (/home/gbusey/file.js:5:3)
// at Object.<anonymous> (/home/gbusey/file.js:10:1)
// at Module._compile (module.js:456:26)
// at Object.Module._extensions..js (module.js:474:10)
// at Module.load (module.js:356:32)
// at Function.Module._load (module.js:312:12)
// at Function.Module.runMain (module.js:497:10)
// at startup (node.js:119:16)
// at node.js:906:3
Информация о местоположении будет одной из следующих:
-
native, если кадр представляет вызов внутри V8 (как в[].forEach). -
plain-filename.js:line:column, если кадр представляет вызов внутри Node.js. -
/absolute/path/to/file.js:line:column, если кадр представляет вызов в пользовательской программе или ее зависимостях.
Строка, представляющая стековый след, генерируется лениво, когда свойство error.stack обращается.
Количество кадров, захваченных стековым следом, ограничено меньшим значением из Error.stackTraceLimit или количеством доступных кадров в текущем цикле обработки событий.
Ошибки системного уровня генерируются как расширенные экземпляры Error, которые подробно описаны здесь.
Класс: RangeError
Подкласс Error, указывающий, что переданный аргумент не находится в наборе или диапазоне допустимых значений для функции; будь то числовой диапазон или диапазон значений вне набора параметров данной функции.
Например:
require('net').connect(-1);
// throws "RangeError: "port" option should be >= 0 and < 65536: -1"
Node.js будет генерировать и выбрасывать экземпляры RangeError немедленно как форму проверки аргументов.
Класс: ReferenceError
Подкласс Error, указывающий на попытку доступа к переменной, которая не определена. Такие ошибки обычно указывают на опечатки в коде или на иные ошибки программы.
Хотя клиентский код может генерировать и распространять эти ошибки, на практике это делает только V8.
doesNotExist; // throws ReferenceError, doesNotExist is not a variable in this program.
За исключением случаев, когда приложение динамически генерирует и выполняет код, экземпляры ReferenceError всегда должны рассматриваться как ошибка в коде или его зависимостях.
Класс: SyntaxError
Подкласс Error, указывающий, что программа не является корректным JavaScript-кодом. Эти ошибки могут быть сгенерированы и распространены только в результате оценки кода. Оценка кода может произойти в результате eval, Function, require, или vm. Эти ошибки почти всегда указывают на ошибку в программе.
try {
require('vm').runInThisContext('binary ! isNotOk');
} catch (err) {
// err will be a SyntaxError
}
Экземпляры SyntaxError невосстановимы в контексте, в котором они были созданы — их можно перехватить только другими контекстами.
Класс: TypeError
Подкласс Error, указывающий, что переданный аргумент не является допустимым типом. Например, передача функции в параметр, ожидающий строку, будет считаться TypeError.
require('url').parse(() => { });
// throws TypeError, since it expected a string
Node.js будет генерировать и выбрасывать экземпляры TypeError немедленно как форму проверки аргументов.
Исключения против ошибок
JavaScript-исключение — это значение, которое выбрасывается в результате недопустимой операции или в качестве целевого значения для оператора throw. Хотя не требуется, чтобы эти значения были экземплярами Error или классов, унаследованных от Error, все исключения, выбрасываемые Node.js или JavaScript-средой выполнения, будут экземплярами Error.
Некоторые исключения невосстановимы на уровне JavaScript. Такие исключения всегда приводят к завершению процесса Node.js. Примеры включают проверки assert() или вызовы abort() на уровне C++.
Системные ошибки
Системные ошибки генерируются, когда возникают исключения в среде выполнения программы. Как правило, это операционные ошибки, возникающие, когда приложение нарушает ограничение операционной системы, например, пытается прочитать файл, который не существует, или когда у пользователя недостаточно прав.
Системные ошибки обычно генерируются на уровне вызова системных функций: исчерпывающий список кодов ошибок и их значений доступен путем выполнения man 2 intro или man 3 errno на большинстве Unix-систем; или онлайн.
В Node.js системные ошибки представлены как расширенные объекты Error с добавленными свойствами.
Класс: System Error
error.code
Свойство error.code — это строка, представляющая код ошибки, которая всегда E за которой следует последовательность заглавных букв.
error.errno
Свойство error.errno — это число или строка. Число — это отрицательное значение, соответствующее коду ошибки, определенному в libuv Error handling. Подробности см. в файле заголовков uv-errno.h (deps/uv/include/uv-errno.h в дереве исходных кодов Node.js). В случае строки она такая же, как error.code.
error.syscall
Свойство error.syscall — это строка, описывающая syscall, который завершился ошибкой.
error.path
Если свойство присутствует (например, в fs или child_process), свойство error.path — это строка, содержащая соответствующий недопустимый путь к файлу.
error.address
Если свойство присутствует (например, в net или dgram ), свойство error.address — это строка, описывающая адрес, подключение к которому завершилось ошибкой.
error.port
Если свойство присутствует (например, в net или dgram ), свойство error.port — это число, представляющее порт соединения, который недоступен.
Общие системные ошибки
Этот список не является исчерпывающим, но перечисляет многие из распространённых системных ошибок, возникающих при написании программы Node.js. Исчерпывающий список можно найти здесь.
-
EACCES(Разрешение отказано): Попытка доступа к файлу, запрещенная правами доступа. -
EADDRINUSE(Адрес уже используется): Попытка привязать сервер (net,httpилиhttps) к локальному адресу закончилась неудачей, так как другой сервер на локальной системе уже использует этот адрес. -
ECONNREFUSED(Подключение отказано): Подключение не удалось, так как целевой компьютер его активно отклонил. Это обычно происходит при попытке подключения к сервису, который неактивен на удаленном хосте. -
ECONNRESET(Подключение прервано удаленной стороной): Подключение было насильственно закрыто удаленной стороной. Это обычно происходит из-за потери подключения на удаленном сокете из-за таймаута или перезагрузки. Часто встречается в модуляхhttpиnet. -
EEXIST(Файл существует): Существующий файл был целью операции, требующей, чтобы файла не существовало. -
EISDIR(Это директория): Операция ожидала файл, но заданный путь указывал на директорию. -
EMFILE(Слишком много файлов открыто в системе): Максимальное количество дескрипторов файлов, разрешенных в системе, достигнуто, и запросы на получение нового дескриптора не могут быть выполнены, пока не будет закрыт хотя бы один. Это встречается при одновременном открытии многих файлов, особенно на системах (в частности, macOS), где есть низкий лимит дескрипторов файлов для процессов. Для исправления низкого лимита выполнитеulimit -n 2048в той же оболочке, что и процесс Node.js. -
ENOENT(Файл или директория не найдены): Обычно возникает при операцияхfsдля указания, что компонент заданного пути не существует — по указанному пути не найдено ни одного объекта (файла или директории). -
ENOTDIR(Это не директория): Компонент заданного пути существовал, но не был директорией, как ожидалось. Обычно возникает приfs.readdir. -
ENOTEMPTY(Директория не пуста): Директория с записями была целью операции, требующей пустой директории — обычноfs.unlink. -
EPERM(Операция запрещена): Попытка выполнения операции, требующей повышенных привилегий. -
EPIPE(Разрыв канала): Запись в канал, сокет или FIFO, для которого нет процесса чтения данных. Часто встречается на уровняхnetиhttp, указывая, что удаленная сторона потока, в который производится запись, закрыта. -
ETIMEDOUT(Операция истекла): Запрос подключения или отправки не удался, так как подключенная сторона не ответила должным образом в течение определенного времени. Обычно встречается уhttpилиnet— часто является признаком того, чтоsocket.end()не был правильно вызван.
© Joyent, Inc. and other Node contributors
Licensed under the MIT License.
Node.js is a trademark of Joyent, Inc. and is used with its permission.
We are not endorsed by or affiliated with Joyent.
https://nodejs.org/dist/latest-v6.x/docs/api/errors.html