在NodeJS中编写域感知函数

我试图想出一个在NodeJS中处理exception的一般(自以为是)的方式,由于性能的影响,它不使用try catch。 我也想远离像简化这样的库,试图使asynchronous代码看起来像同步代码。

似乎域名很适合这个法案,但是我想征求意见/build议,以便我build议使用它们。 这种方法是否存在重大问题?

我打算使我的大部分asynchronous函数遵循以下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); })); }); } 

我想要做的是避免在asynchronous函数中引发错误。 这主要是针对我没有任何我想要处理的特定exception的情况,但是我想确保exception不会“丢失”。

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

 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"); }); 

这个人为的例子运行良好,但更复杂的场景有很多function呢。

更新:#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); } } 

我会对这两种方法做一些性能testing,看看有没有什么区别。

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

 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); })); }); } 

我喜欢基于域的版本的简单性,但是它或多或less是等价的? 当然,你需要记住使用d.intercept或者检查任何callback的错误,但是我可以处理它。

UPDATE

我已经做了一些更多的工作,并发现了一个使用域来编写asynchronous函数的好方法,它消除了大部分的exception处理样板,并且在某些情况下执行比尝试exception处理更好:

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

这取代了下面这篇文章中的大部分内容。 实际上我提出的函数useExistingDomainifAvailable在我写这个原始答案的时候没有考虑到副作用。 主要的一点是error handling总是通过短路回到域exception处理程序而不是通过callback链完成的。

UPDATE

所以,我做了一些性能testing,发现域的版本实际上和将函数体封装在try catch中一样:

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

 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); } } 

testing为:

 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.jstesting结果如下:

对照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

显示tryCatchOuterscheme的显着性能增益。

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

 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 ops / sec±1.06%(39次取样)
useExistingDomainifAvailable x 42.86 ops / sec±6.81%(38次采样)
tryCatchOuter x 95.86 ops / sec±2.35%(68次采样)
domainWithOuter x 94.65 ops / sec±1.91%(67次采样)
最快的是tryCatchOuter,domainWithOuter

所以在这种情况下,基本上使用域与使用try catch在性能方面是一样的,在语法上有一些差异。

我猜是因为domain.run和doman.intercept使用try catch来覆盖它们,所以需要以相同的方式使用相同的性能告诫。