Книга: Выразительный JavaScript
Обработка ошибок
Обработка ошибок
В коде файлового сервера есть шесть мест, где мы перенаправляем исключения, когда мы не знаем, как обрабатывать ошибки. Поскольку исключения не передаются автоматически в функции обратного вызова, но передаются им как аргументы, их надо каждый раз обрабатывать персонально. Это сводит на нет преимущество обработки исключений, а именно, возможность централизованно обрабатывать ошибки.
Что будет, когда что-то реально выбросит исключение в системе? Мы не используем блоки try
, потому оно будет передано на самый верх стека вызовов. В Node это приводит к прекращению выполнения программы и выводу информации об исключении (вместе с отслеживанием стека) на стандартный вывод.
Поэтому наш сервер будет падать при возникновении проблем в коде – в отличие от проблем с асинхронностью, которые будут переданы как аргументы в функции вызова. Если нам надо обрабатывать все исключения, возникающие при обработке запроса, чтобы мы точно отправили ответ, нам надо добавлять блоки try/catch
в каждом обратном вызове.
Это плохо. Много программ для Node написаны так, чтобы использовать как можно меньше работы с исключениями, подразумевая что в случае возникновения исключения программа не может его обработать, и поэтому надо падать.
Ещё один подход – использование обещаний, которые были описаны в главе 17. Они ловят исключения, выброшенные функциями обратного вызова и передают их как ошибки. В Node можно загрузить библиотеку promise
и использовать её для обработки асинхронных вызовов. Немногие библиотеки Node интегрируют обещания, но обычно их довольно просто обернуть. Отличный модуль “promise”
с NPM содержит функцию denodeify
, которая берёт асинхронную функцию вроде fs.readFile
и преобразовывает её в функцию, возвращающую обещание.
var Promise = require("promise");
var fs = require("fs");
var readFile = Promise.denodeify(fs.readFile);
readFile("file.txt", "utf8").then(function(content) {
console.log("The file contained: " + content);
}, function(error) {
console.log("Failed to read file: " + error);
});
Для сравнения, я написал ещё одну версию файлового сервера с использованием обещаний, которую можно найти на eloquentjavascript.net/code/file_server_promises.js. Она почище, потому что функции теперь могут возвращать результаты, а не назначать обратные вызовы, и исключения передаются неявно.
Приведу несколько строк оттуда, чтобы продемонстрировать разницу в стилях.
Объект fsp
, использующийся в коде, содержит варианты функций fs
с обещаниями, обёрнутыми при помощи Promise.denodeify
. Возвращаемый из обработчика метода объект, со свойствами code
и body
, становится окончательным результатом цепочки обещаний, и он используется для определения того, какой ответ отправить клиенту.
methods.GET = function(path) {
return inspectPath(path).then(function(stats) {
if (!stats) // Does not exist
return {code: 404, body: "File not found"};
else if (stats.isDirectory())
return fsp.readdir(path).then(function(files) {
return {code: 200, body: files.join("n")};
});
else
return {code: 200,
type: require("mime").lookup(path),
body: fs.createReadStream(path)};
});
};
function inspectPath(path) {
return fsp.stat(path).then(null, function(error) {
if (error.code == "ENOENT") return null;
else throw error;
});
}
Функция inspectPath
– простая обёртка вокруг fs.stat
, обрабатывающая случай, когда файл не найден. В этом случае мы заменяем ошибку на успех, возвращающий null
. Все остальные ошибки можно передавать. Когда обещание, возвращаемое из этих обработчиков, обламывается, сервер отвечает кодом 500.
- Перехват ошибок
- Обработка перехваченных ошибок
- ГЛАВА 4 Обработка исключений
- B1.7. Функции обработки ошибок
- 26.7. Обработка дополнительных аргументов
- Проверка диска на наличие ошибок
- А.2. Поиск ошибок в динамической памяти
- Векторная обработка исключений
- Обработка исключений
- 2.2. Базовая обработка командной строки
- Пример: обработка ошибок
- Пример: обработка ошибок как исключений