1

我正在尝试提出一种处理 NodeJS 中异常的通用(自以为是)方法,由于性能下降,该方法不使用 try catch。我也想远离像 streamline 这样试图使异步代码看起来像同步代码的库。

似乎域很符合要求,但我想就我提议的使用方式征求意见/建议。这种方法是否存在重大问题?

我计划让我的大部分异步函数遵循以下 domainAware 函数的模式:

function errorinAsync(options, callback){
    options = options || {};
    setTimeout(function(){
        return callback(new Error("This should be caught"));
    },1000);

}
function domainAware(options, callback){
    if(domain.active){
        d = domain.active;
    }else{
        d = domain.create();
        d.on('error', function(err){
            return callback(err);
        });
    }

    d.run(function(){
        //Some Synchronous code that might throw an exception;
        var a = {b: 1, c: 2};
        var thing = JSON.stringify(a);
        errorinAsync(null,d.intercept(function(err) {
            return callback(null);
        }));
    });
}

我想要做的是避免在异步函数中抛出错误。这主要适用于我没有要处理的任何特定异常但我想确保异常不会“丢失”的情况。

我可以用域上下文调用它:

var d = domain.create();
d.on('error', function(er) {
  console.error('Caught error!', er);
});
d.run(function() {
    domainAware(null, d.intercept(function(err) {
        console.log("all Done");
    }));
});

或者没有:

domainAware(null, function(err){
    if(err){
        return console.log("Caught Error from Callback" + err);
    }
    console.log("all Done");
});

这个人为的例子效果很好,但是对于具有许多功能的更复杂的场景呢?

更新:#1

使用 try catch 的等效函数可能是:

function noHandling(callback){
    var a = {b: 1, c: 2};
    var thing = JSON.stringify(a);
    errorinAsync(null,function(err) {
        if(err) return callback(err);
        return callback(null);
    });
}

function notDomainAware(options, callback){
    try{
        noHandling(callback);
    }catch(err){
        callback(err);
    }
}

我将对这两种方法进行一些性能测试,看看是否有任何区别。

除了性能之外,使用基于域的方法还有其他问题吗?域感知功能的修订版本可能如下所示。

function domainAware(options, callback){
    var d = domain.active || domain.create().on('error', function(err){ return callback(err); });

    d.run(function(){
        //Some Synchronous code that might throw an exception;
        var a = {b: 1, c: 2};
        var thing = JSON.stringify(a);
        errorinAsync(null,d.intercept(function(err) {
            return callback(null);
        }));
    });
}

我喜欢基于域的版本的简单性,但它或多或少是等价的吗?当然,您确实需要记住使用 d.intercept 或检查任何回调的错误,但我可以处理。

4

1 回答 1

2

更新

我在这方面做了更多的工作,并找到了一种使用域编写异步函数的好方法,它消除了大部分异常处理样板文件,并且在某些情况下比 try catch 异常处理执行得更好:

http://www.lighthouselogic.com/using-a-new-domain-for-each-async-function-in-node/

这取代了下面帖子中所写的大部分内容。事实上,我在下面提出的函数 useExistingDomainifAvailable 具有我在编写这个原始答案时没有考虑的副作用。主要的是错误处理总是通过返回域异常处理程序的短路而不是通过回调链来完成。

更新

所以,我做了一些性能测试,发现域版本实际上与将函数体包装在 try catch 中大致相同:

我的所有测试中都使用了以下两个函数:

function doSomethingAsync(options, callback){
    options = options || {};
    setTimeout(function(){
        return callback(null);
    },1);

}

function callThroughDomain(fn, callback) {
    var d = domain.create();
    d.on('error', function(er) {
      console.error('Caught error!', er);
    });
    d.run(function() {
        fn(1000000, d.intercept(callback));
    });
}

我从一个控件开始:

function tryCatchCallback(j, callback) {
        try{    
            var s = 0;
            for (var i = 0; i < j; i++) s = i;
            for (var i = 0; i < j; i++) s = i;
            for (var i = 0; i < j; i++) s = i;
            for (var i = 0; i < j; i++) s = i;
            doSomethingAsync(null, function(err){
                //Just like domain.intercept, exceptions in the callback are handled
                try{
                    if(err) return callback(err);
                    callback(s);
                }catch(ex){
                    callback(ex);
                }

            });
        }
        catch(ex) {
            callback(ex);
        }
}

测试是:

callThroughDomain(tryCatchCallback, function(){
    deferred.resolve();
});

然后我尝试使用预先声明的域:

function useExistingDomainifAvailable(j, callback) {
    var d = domain.active || domain.create().on('error', function(err){ return callback(err); });

    d.run(function(){
        var s = 0;
        for (var i = 0; i < j; i++) s = i;
        for (var i = 0; i < j; i++) s = i;
        for (var i = 0; i < j; i++) s = i;
        for (var i = 0; i < j; i++) s = i;
        doSomethingAsync(null, d.intercept(function(err){
            callback(s);
        }));
    });
}

callThroughDomain(useExistingDomainifAvailable, function(){
    deferred.resolve();
});

然后我尝试使用外部 try catch 调用的函数的胆量

function tryCatchOuter(j, callback) {
    try{
        outer(1000000, callback);
    }catch(e){
        console.log(e);
    }
}

function outer(j, callback) {
    var s = 0;
    for (var i = 0; i < j; i++) s = i;
    for (var i = 0; i < j; i++) s = i;
    for (var i = 0; i < j; i++) s = i;
    for (var i = 0; i < j; i++) s = i;
    doSomethingAsync(null, function(err){
        //Again catching errors from callback
        try{
            if(err) return callback(err);
            callback(s);
        }catch(ex){
            callback(ex)
        }

    });
}

callThroughDomain(tryCatchOuter, function(){
    deferred.resolve();
});

我的 benchmark.js 测试结果如下:

控制 x 42.12 ops/sec ±0.83%(采样 38 次运行)
useExistingDomainifAvailable x 41.98 ops/sec ±6.67%(采样 44 次运行)
tryCatchOuter x 93.23 ops/sec ±2.07%(采样 66 次运行)
最快的是 tryCatchOuter

显示 tryCatchOuter 场景的显着性能提升。

最后比较尝试使用外部函数体的域

function domainWithOuter(j, callback) {
    var d = domain.active || domain.create().on('error', function(err){ return callback(err); });

    d.run(function(){
        outerNoHandler(j,callback);
    });
}

function outerNoHandler(j, callback) {
    var s = 0;
    for (var i = 0; i < j; i++) s = i;
    for (var i = 0; i < j; i++) s = i;
    for (var i = 0; i < j; i++) s = i;
    for (var i = 0; i < j; i++) s = i;
    doSomethingAsync(null, function(err){
            //Don't need try catch here 
            //Exceptions managed by domain
            if(err) return callback(err);
            callback(s);
    });
}

控制 x 42.75 操作/秒 ±1.06%(采样 39 次运行)
useExistingDomainifAvailable x 42.86 操作/秒 ±6.81%(采样 38 次运行)
tryCatchOuter x 95.86 操作/秒 ±2.35%(采样 68 次运行)
domainWithOuter x 94.65 操作/秒 ±1.91 %(采样 67 次运行)
最快的是 tryCatchOuter,domainWithOuter

因此,在这种情况下,就性能而言,本质上使用域与使用 try catch 相同,但语法有所不同。

我猜是因为 domain.run 和 doman.intercept 在幕后使用 try catch,它们需要以类似的方式使用,并具有相同的性能警告。

于 2013-07-06T11:19:17.370 回答