Forrest L Norvell & Domenic Denicola, nodeconf 2013
uncaughtException
is gross
var domain = require("domain");
var d = domain.create();
d.on("error", function (error) {
console.error("Well, this sucks: %s", error.message);
process.exit(1);
});
d.run(function () {
// do error-prone stuff here
});
var http = require('http'), domain = require('domain');
http.createServer(function (req, res) {
var d = domain.create();
d.on("error", function (error) {
res.writeHead(500, {'Content-Type': 'text/plain'});
res.end('Couldn\'t fulfill request because of an error: ' +
error.message);
});
d.run(function () {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World\n');
});
}).listen(1337, '127.0.0.1');
console.log('Server running at http://127.0.0.1:1337/');
domain.add(emitter)
binds an EventEmitter to a domain.
var http = require('http'), domain = require('domain');
http.createServer(function (req, res) {
var d = domain.create();
d.on("error", function (error) { /* ... */ });
d.add(req);
d.add(res);
d.run(function () { /* ... */ });
}).listen(1337, '127.0.0.1');
app.use(function (req, res, next) {
var d = domain.create();
d.on("error", next);
d.run(next);
});
// all the other middlewares in the stack
app.use(express.errorHandler());
One nice thing is that the built-in error handler respects the
Accept
header coming from the user agent.
var defaultHandler = express.errorHandler();
app.use(function (error, req, res, next) {
if (domain.active) {
domain.active.emit("error", error);
} else {
defaultHandler(error, req, res, next);
}
});
app.get("/crashy", function (req, res) {
var d = domain.create();
d.on("error", function (error) {
res.send(500, {"error": error.message});
});
d.run(function () { throw new Error("oops"); });
});
domain.bind(fn)
ensures that any errors inside
fn
are trapped by the domain.domain.intercept(fn)
is the same, but takes a
conventional Node callback and traps the error parameter in the
domain. The parameter must be an instance of
Error.
var d = domain.create();
// ...
fs.readFile("filename.txt", function (error, data) {
if (error) return d.emit("error", error);
// do stuff with contents
});
becomes
var d = domain.create();
// ...
fs.readFile("filename.txt", d.intercept(function (data) {
// do stuff with contents
}));
domain.bind(callback)
(or
domain.intercept(callback)
) to trap errors in your
code.domain.run()
, but you'll still want to explicitly bind
your application callbacks to the lexically closest domain
var pool;
var poolD = domain.create(); poolD.run(function () {
pool = new mydb.Pool();
poolD.on("error", function (error) {
console.error("Connection pool exploded with '%s'. Trying clean shutdown.", error.message);
pool.flush();
pool.close(function () { process.exit(1); });
});
});
var client = require("./mydb/client.js")(pool);
function processUsers(error, users) { /* do stuff */ }
function getUsers() {
var d = domain.create();
d.on("error", function (error) { /* custom error-handling */ });
client.getUsers(d.bind(processUsers));
}
domain.run()
, domain.bind()
,
domain.add()
, and domain.intercept()
you can
handle all three of the common mechanisms for signaling errors in node
(callback convention, error events, and try/catch).domain.enter()
and domain.exit()
are the primitives
that set up and tear down domains.domain.exit()
gets called both
when errors are trapped and when domains are exited normally.domain.enter()
and domain.exit()
are the primitives
domain.bind()
is a wrapper around
Function.bind()
, domain.enter()
, and
domain.exit()
:
Domain.prototype.bind = function (callback) {
return function () {
this.enter();
callback();
this.exit();
}.bind(this);
};
Domain.prototype.intercept = function (callback) {
return function (error) {
if (error) return this.emit("error", error);
this.enter();
callback.apply(null, Array.prototype.slice.apply(arguments, 1));
this.exit();
}.bind(this);
}
domain.run()
is just an immediately invoked
version of domain.bind()
.domain.enter()
pushes a domain on the stack,
domain.exit()
pops that domain and all the domains
above it on the stackthrow
pops multiple stack frames in traditional exception handlingprocess.domain
and domain.active
process.domain
in code where
domains are optional or can be used opportunisticallydomain.active