Асинхронная обработка исключений Javascript с помощью node.js

В настоящее время я работаю над приложением node.js, и у меня обычная проблема с асинхронным кодом.

Я реализую сервисный сервер поверх HTTP-модуля Node.

Этот сервер поддерживает маршруты (express like). Например, у меня есть код, который выглядит так:

server.any("/someRoute",function(req,resp){
    resp.end("this text is sent to clients via http")
});

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

server.any("/someRoute",function(req,resp){
    setTimeout(function(){
        throw new Error("This won't get caught");
    },100);
});

Я не понимаю, как я могу поймать ошибку здесь. Я не хочу крашить сервер из-за одного сбоя на стороне сервера, вместо этого я хочу обслуживать 500.

Единственные решения, которые я смог придумать, на самом деле не выразительны. Я придумал использовать только process.on("uncaughtException",callback) и аналогичный код с использованием узла 0.8 Domains (что является частичным решением, но домены в настоящее время глючат, и это все еще не очень выразительно, так как в конечном итоге мне приходится создавать домен для каждого дескриптора).

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

Это возможно? Как лучше всего обрабатывать ошибки в этом случае?

Я хотел бы подчеркнуть, что он должен иметь возможность продолжать обслуживать запросы после плохих запросов, и перезапуск сервера при каждом запросе или создание доменов для каждого обработчика и перехват их неперехваченных исключений кажется мне плохой идеей. Кроме того, я слышал, что обещания могут помочь мне (что-то о throw в обещаниях), могут ли обещания помочь мне в этой ситуации?


person Benjamin Gruenbaum    schedule 13.01.2013    source источник
comment
Насколько мне известно, перехват выброшенных исключений из асинхронного кода невозможен за пределами чего-то вроде доменов (что звучит точно так же, как привязка всех выброшенных ошибок из функции к определенной функции обработчика); альтернативой является использование стандартного стиля обратного вызова Node.js, где первым параметром является ошибка, а не выброс (именно поэтому этот шаблон так распространен).   -  person Michelle Tilley    schedule 13.01.2013
comment
Я должен уметь работать с кодом, который не писал сам. Решение мечты было бы чем-то вроде functionName.exceptionHandler = someFunction(exception) или что-то в этом роде.   -  person Benjamin Gruenbaum    schedule 13.01.2013
comment
Полностью понимаю, но я не уверен, что вы легко это поймете. Вы должны перехватывать вызовы (это то, что домены пытаются сделать для встроенных генераторов событий). (Возможно, какое-то теоретическое расширение JavaScript — скажем, CoffeeScript — могло бы выполнить для вас необходимую генерацию кода.) Дело в том, что throwing из асинхронного кода — дурной тон, и, например, там, где ничего не поделаешь (возможно, JSON.parse терпит неудачу и т. д.), событие uncaughtException — это ваш билет к сохранению вашего приложения (хотя для библиотеки, безусловно, было бы лучше catch внутри собственного кода).   -  person Michelle Tilley    schedule 13.01.2013
comment
@BrandonTilley Буду признателен за ваши отзывы о моем собственном ответе.   -  person Benjamin Gruenbaum    schedule 13.01.2013
comment
@BenjaminGruenbaum Можете ли вы ответить на этот stackoverflow.com/questions/43069522/   -  person user7350714    schedule 28.03.2017


Ответы (1)


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

Promise.try(function(){ 
    throw new Error("Something");
}).catch(function(err){
    console.log(err.message); // logs "Something"
});

С тайм-аутом (обратите внимание, что мы должны вернуть Promise.delay):

Promise.try(function() {
    return Promise.delay(1000).then(function(){
        throw new Error("something");
    });
}).catch(function(err){
    console.log("caught "+err.message);
});

С общей функцией NodeJS:

var fs = Promise.promisifyAll("fs"); // creates readFileAsync that returns promise
fs.readFileAsync("myfile.txt").then(function(content){
    console.log(content.toString()); // logs the file's contents
    // can throw here and it'll catch it
}).catch(function(err){
    console.log(err); // log any error from the `then` or the readFile operation
});

Этот подход является одновременно быстрым и безопасным. Я рекомендую его выше приведенного ниже ответа, в котором используются домены, которые, вероятно, не останутся здесь.


Я закончил тем, что использовал домены, я создал следующий файл, который я назвал mistake.js, который содержит следующий код:

var domain=require("domain");
module.exports = function(func){
    var dom = domain.create();
    return { "catch" :function(errHandle){
        var args = arguments;
        dom.on("error",function(err){
            return errHandle(err);
        }).run(function(){
            func.call(null, args);
        });
        return this;
    };
};

Вот пример использования:

var atry = require("./mistake.js");

atry(function() {
    setTimeout(function(){
        throw "something";
    },1000);
}).catch(function(err){
    console.log("caught "+err);
});

Он также работает как обычный catch для синхронного кода.

atry(function() {
    throw "something";
}).catch(function(err){
    console.log("caught "+err);
});

Буду признателен за отзыв о решении

Кстати, в версии 0.8, по-видимому, когда вы перехватываете исключение в домене, оно все равно выдает process.on("uncaughtException"). Я имел дело с этим в моем process.on("uncaughtException") с

 if (typeof e !== "object" || !e["domain_thrown"]) {

Тем не менее, документация предлагает против process.on("uncaughtException") в любом случае

person Benjamin Gruenbaum    schedule 13.01.2013
comment
Я думаю, что это очень хорошо - сохраняет все создание домена на своем месте. - person Michelle Tilley; 14.01.2013
comment
Очень красиво, мне нравится ваша идея! - person RushPL; 30.01.2014
comment
Я реализовал вашу идею в модуле, опубликованном на npm github.com/CodeCharmLtd/atry - npm install atry - person RushPL; 30.01.2014
comment
@RushPL очень мило! Справедливости ради следует отметить, что ядро ​​NodeJS не очень хорошо очищает себя после бросков, поэтому в основном это следует использовать в пользовательском коде. У промисов есть гарантия броска (вы можете добавить их!), обещанный код безопасен от броска и с тех пор, как Bluebird оказалось, что они такие же быстрые, как обратные вызовы, поэтому сейчас я использую их в своем коде. Это тоже будет работать очень похоже Promise.try(function(){ return Promise.delay(30).then(function(){ throw new Error("Hi");});}).catch(function(err){ console.log(err); }); - person Benjamin Gruenbaum; 30.01.2014
comment
Похоже, хорошая библиотека, которую стоит проверить при разработке будущих API! Я думаю, что atry по-прежнему может быть полезен при работе с существующими API-интерфейсами или API-интерфейсами узлов - просто нужно большое предупреждение о возможной утечке ресурсов, поэтому я добавлю ссылку на это stackoverflow.com/questions/15825752/ - person RushPL; 30.01.2014
comment
Что это за идея домена? - person CMCDragonkai; 17.04.2015
comment
@BenjaminGruenbaum, можете ли вы ответить на этот stackoverflow.com/questions/43069522/ - person user7350714; 28.03.2017