UPDATE
I did some more work on this and found a great way to write asynchronous functions using domains that eliminate most exception handling patterns and work better than trying to handle exception exceptions in some cases:
http://www.lighthouselogic.com/using-a-new-domain-for-each-async-function-in-node/
This replaces much of what is written in the message below. In fact, the useExistingDomainifAvailable function, which I suggested below, has side effects that I did not take into account when writing this original answer. The main thing is that error handling is always performed through a short circuit back to the domain exception handler, and not through the callback chain.
UPDATE
So, I did some performance testing and found that the domain version is actually about the same as packing the function body in try catch:
In all my tests, the following two functions were used:
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)); }); }
I started with the control:
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){
The test for which was:
callThroughDomain(tryCatchCallback, function(){ deferred.resolve(); });
Then I tried using the previously declared domain:
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(); });
Then I tried with brushes a function called by an external catch attempt
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){
The results of my benchmark.js tests are as follows:
control x 42.12 ops / sec ยฑ 0.83% (sample of 38 samples)
useExistingDomainifAvailable x 41.98 ops / sec ยฑ 6.67% (samples in 44 passes)
tryCatchOuter x 93.23 ops / sec ยฑ 2.07% (66 samples)
The fastest - tryCatchOuter
Displays significant performance gains for the tryCatchOuter script.
And for the final comparison, we try domains with the external body of the function
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){
control x 42.75 op / s ยฑ 1.06% (samples in 39 runs)
useExistingDomainifAvailable x 42.86 ops / sec ยฑ 6.81% (38 samples)
tryCatchOuter x 95.86 ops / sec ยฑ 2.35% (68 passes selected)
domainWithOuter x 94.65 ops / sec ยฑ 1.91% (sampling 67)
The fastest - tryCatchOuter, domainWithOuter
Thus, essentially the use of the domain is the same as using try catch in terms of performance in this case, with some differences in syntax.
I think because domain.run and doman.intercept use try catch under covers, they should be used in the same way with the same caveats about performance.