Search code examples
node.jserror-handlingnode.js-domains

Can domains be nested in node.js?


With synchronous errors, you can nest error scopes like this:

try {
  try {
    throw Error('e')
  } catch(e) {
    if(e.message !== 'f')
      throw e
  }
} catch(e) {
  handleError(e)
}

This is how I would expect it to work, but it doesn't (seems an error inside a domain error handler is thrown up to the top, skipping any domains in between):

var domain = require('domain');
var dA = domain.create();
dA.on('error', function(err) {
    console.log("dA: "+ err); // never happens
});
dA.run(function() {
    var dB = domain.create();
    dB.on('error', function(err) {
        throw err
    });
    dB.run(function() {
        setTimeout(function() {
            console.log('dB')
            throw 'moo'
        },0)
    });
});

Is there a way to do this right?


Solution

  • Bubbling doesn't work in domains through rethrowing. If you want to pass an error off to another domain you know can handle an error, you can re-emit the error event on that domain directly:

    var domain = require('domain');
    var dA = domain.create();
    dA.on('error', function(err) {
        console.log("dA: "+ err); // never happens
    });
    dA.run(function() {
        var dB = domain.create();
        dB.on('error', function(err) {
            dA.emit('error', err);
        });
        dB.run(function() {
            setTimeout(function() {
                console.log('dB')
                throw 'moo'
            },0)
        });
    });
    

    To expand a little, the problem with throwing from a domain's error handler is that it propagates directly to the top level, and even more confusingly, if the throw is the result of an error in the error handlier, is that the stacktrace that gets printed out is from your original error, not the new error in the handler. Theoretically it would be possible to bubble exceptions up the stack, but that's not how domains were designed.

    The "nested" domains will work properly if the handler of an outer domain throws while an inner domain is active, but what it does in that case is give the error to the outer domain's error handler and then exits both the outer and the nested domain. This mimics how a catch unwinds the stack in the try/catch case, but it can be a little confusing.