Ошибки
Приложения, работающие в Node.js, обычно сталкиваются с четырьмя категориями ошибок:
- Стандартные ошибки JavaScript, такие как:
-
<EvalError> : возникает при вызове
eval(). - <SyntaxError> : возникает из-за неправильного синтаксиса языка JavaScript.
- <RangeError> : возникает, когда значение не попадает в ожидаемый диапазон.
- <ReferenceError> : возникает при использовании неопределенных переменных.
- <TypeError> : возникает при передаче аргументов неправильного типа.
- <URIError> : возникает при неправильном использовании функций обработки глобальных URI.
-
<EvalError> : возникает при вызове
- Системные ошибки, вызванные ограничениями операционной системы, например, попытка открыть несуществующий файл или отправить данные по закрытому сокету.
- Ошибки, заданные пользователем, вызванные кодом приложения.
- Ошибки проверки (Assertion Errors) — это особый класс ошибок, которые могут возникать, когда 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); -
Несколько типично асинхронных методов в Node.js API могут все еще использовать механизм
throwдля вызова исключений, которые необходимо обработать с помощьюtry / catch. Нет исчерпывающего списка таких методов; обратитесь к документации каждого метода, чтобы определить необходимый механизм обработки ошибок.
Использование механизма событий 'error' наиболее распространено для API на основе потоков (stream-based) и эмиттеров событий (event emitter-based), которые сами по себе представляют серию асинхронных операций во времени (в отличие от одной операции, которая может преуспеть или потерпеть неудачу).
Для всех 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.log(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. Стек вызовов распространяется только до начала синхронного выполнения кода или до количества кадров, заданного свойством 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 all frames above 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.log(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 should be > 0 && < 65536
Node.js будет генерировать и выбрасывать экземпляры RangeError немедленно как форму проверки аргументов.
Класс: ReferenceError
Подкласс Error, указывающий на попытку доступа к переменной, которая не определена. Такие ошибки обычно указывают на опечатки в коде или иную ошибку в программе.
Хотя клиентский код может генерировать и распространять эти ошибки, на практике это делает только V8.
doesNotExist; // throws ReferenceError, doesNotExist is not a variable in this program.
Экземпляры ReferenceError будут иметь свойство error.arguments, значение которого является массивом, содержащим один элемент: строку, представляющую переменную, которая не была определена.
const assert = require('assert');
try {
doesNotExist;
} catch(err) {
assert(err.arguments[0], 'doesNotExist');
}
Если приложение не генерирует и не выполняет код динамически, экземпляры 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 с добавленными свойствами.
Класс: Системная ошибка
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 — это строка, описывающая системный вызов, который завершился неудачей.
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(Слишком много файлов открыто в системе): Максимальное количество разрешенных дескрипторов файлов в системе достигнуто, и запросы на открытие нового дескриптора не могут быть выполнены до тех пор, пока не будет закрыт хотя бы один. Это встречается при одновременном открытии большого количества файлов, особенно на системах (особенно OS X), где предел для дескрипторов файлов на процесс низкий. Для решения проблемы с низким лимитом выполните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-v4.x/docs/api/errors.html