> > Here's some working code:
"use strict"; var domain = require('domain'); var assert = require('assert'); // Implement a session class that keeps track of async operations // Is uses a domain to handle errors in these operations function Session() { this.domain = domain.createDomain(); this.started = 0; this.completed = 0; this.isClosed = false; this.parentDomain = process.domain; this.errors = []; this.results = []; var theSession = this; this.domain.on("error", function(err) { theSession.errors.push(err); operationComplete(theSession); }); } // Add a new (potentially async) operation to the session function addOperation(operation) { this.started++; var session = this; this.domain.run(function() { operation(function(result){ operationComplete(session, result); }); }); } Session.prototype.addOperation = addOperation; // close a session, that is, no longer allow new operation to be added function closeSession(cb) { this.isClosed = true; this.callback = cb; checkSessionComplete(this); } Session.prototype.closeSession = closeSession; // Mark that an operation has completed function operationComplete(session, result) { if (result) { session.results.push(result); } session.completed++; checkSessionComplete(session); } // check whether all operations have completed function checkSessionComplete(session) { if (session.isClosed && session.completed == session.started) { session.parentDomain.run(function() { session.callback(session); }); } } // Create and exercise a session var mainDomain = domain.createDomain(); mainDomain.run(function() { var session = new Session(); for (var i = 0; i < 10; i++) { session.addOperation(function(cb) { var shouldThrow = i % 2 == 1; process.nextTick(function() { if (shouldThrow) { throw new Error(); } else { cb("done"); } }) }); } session.closeSession(allWorkDone) }); function allWorkDone(session) { assert.ok(session.isClosed); assert.equal(session.errors.length, 5); assert.equal(session.results.length, 5); assert.equal(process.domain, mainDomain); console.log("Done."); } This is simplified to save space, so, yes, I know it lacks error-checking and handling of corner cases and that what it does it trivial :-) The idea is that the Session manages a group of async operations. It runs them all under a domain specific to the Session, and any errors caught are simply stored in the Session. When all the operations are complete, the Session calls back into the main logic, which should run under a different domain. As you can see, they way it does this is to save the domain that was active at the time the Session was created and use it for the call back into the main logic (in checkSessionComplete()).. In real life, this is used to orchestrate web service calls. node.js creates an HTTP server. Requests it receives are analyzed and a set of web service calls are made under the control of a Session (which allows them to be run in parallel inserted of series.) When all the web service calls are complete, their responses are merged into the response to node's caller. All of the work to process a node request is done under a request-specific domain (to allow any unhandled errors to be formatted into an HTTP response). The final callback from the Session must be done under that same domain, which, since much async processing has occurred, is no longer in the stack.