Writing Domain Functions in NodeJS

I am trying to come up with a general (stubborn) way to handle exceptions in NodeJS that does not use try catch due to a performance hit. I also want to stay away from libraries like streamline that try to make asynchronous code look like synchronization code.

Domains seem to fit the bill well, but I wanted to invite comments / suggestions on how I suggest using them. Are there significant issues with this approach?

I plan that most of my asynchronous functions follow the domainAware function template below:

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

I want to do this to avoid an error in an asynchronous function. This is mainly for situations where I do not have any special exceptions that I want to handle, but I want the exceptions not to be lost.

I can call it using the domain context:

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

Or without it:

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

This contrived example works well, but what about more complex scenarios with many features.

Update: # 1

An equivalent function using try catch could be:

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

I will conduct some performance tests on these two approaches to see if there is a difference.

Besides performance, is there another problem with using a domain based approach? A revised version of the domain notification feature might look like this.

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

I like the simplicity of a domain-based version, but is it more or less equivalent? Of course, you need to remember to use d.intercept or check for any callback errors, but I can handle this.

+1
source share
1 answer

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

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){ //Again catching errors from callback try{ if(err) return callback(err); callback(s); }catch(ex){ callback(ex) } }); } callThroughDomain(tryCatchOuter, function(){ deferred.resolve(); }); 

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){ //Don't need try catch here //Exceptions managed by domain if(err) return callback(err); callback(s); }); } 

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.

+2
source

Source: https://habr.com/ru/post/1493279/


All Articles