1# Domain 2<!-- YAML 3deprecated: v1.4.2 4changes: 5 - version: v8.8.0 6 description: Any `Promise`s created in VM contexts no longer have a 7 `.domain` property. Their handlers are still executed in the 8 proper domain, however, and `Promise`s created in the main 9 context still possess a `.domain` property. 10 - version: v8.0.0 11 pr-url: https://github.com/nodejs/node/pull/12489 12 description: Handlers for `Promise`s are now invoked in the domain in which 13 the first promise of a chain was created. 14--> 15 16<!--introduced_in=v0.10.0--> 17 18> Stability: 0 - Deprecated 19 20<!-- source_link=lib/domain.js --> 21 22**This module is pending deprecation**. Once a replacement API has been 23finalized, this module will be fully deprecated. Most developers should 24**not** have cause to use this module. Users who absolutely must have 25the functionality that domains provide may rely on it for the time being 26but should expect to have to migrate to a different solution 27in the future. 28 29Domains provide a way to handle multiple different IO operations as a 30single group. If any of the event emitters or callbacks registered to a 31domain emit an `'error'` event, or throw an error, then the domain object 32will be notified, rather than losing the context of the error in the 33`process.on('uncaughtException')` handler, or causing the program to 34exit immediately with an error code. 35 36## Warning: Don't ignore errors! 37 38<!-- type=misc --> 39 40Domain error handlers are not a substitute for closing down a 41process when an error occurs. 42 43By the very nature of how [`throw`][] works in JavaScript, there is almost 44never any way to safely "pick up where it left off", without leaking 45references, or creating some other sort of undefined brittle state. 46 47The safest way to respond to a thrown error is to shut down the 48process. Of course, in a normal web server, there may be many 49open connections, and it is not reasonable to abruptly shut those down 50because an error was triggered by someone else. 51 52The better approach is to send an error response to the request that 53triggered the error, while letting the others finish in their normal 54time, and stop listening for new requests in that worker. 55 56In this way, `domain` usage goes hand-in-hand with the cluster module, 57since the master process can fork a new worker when a worker 58encounters an error. For Node.js programs that scale to multiple 59machines, the terminating proxy or service registry can take note of 60the failure, and react accordingly. 61 62For example, this is not a good idea: 63 64```js 65// XXX WARNING! BAD IDEA! 66 67const d = require('domain').create(); 68d.on('error', (er) => { 69 // The error won't crash the process, but what it does is worse! 70 // Though we've prevented abrupt process restarting, we are leaking 71 // resources like crazy if this ever happens. 72 // This is no better than process.on('uncaughtException')! 73 console.log(`error, but oh well ${er.message}`); 74}); 75d.run(() => { 76 require('http').createServer((req, res) => { 77 handleRequest(req, res); 78 }).listen(PORT); 79}); 80``` 81 82By using the context of a domain, and the resilience of separating our 83program into multiple worker processes, we can react more 84appropriately, and handle errors with much greater safety. 85 86```js 87// Much better! 88 89const cluster = require('cluster'); 90const PORT = +process.env.PORT || 1337; 91 92if (cluster.isMaster) { 93 // A more realistic scenario would have more than 2 workers, 94 // and perhaps not put the master and worker in the same file. 95 // 96 // It is also possible to get a bit fancier about logging, and 97 // implement whatever custom logic is needed to prevent DoS 98 // attacks and other bad behavior. 99 // 100 // See the options in the cluster documentation. 101 // 102 // The important thing is that the master does very little, 103 // increasing our resilience to unexpected errors. 104 105 cluster.fork(); 106 cluster.fork(); 107 108 cluster.on('disconnect', (worker) => { 109 console.error('disconnect!'); 110 cluster.fork(); 111 }); 112 113} else { 114 // the worker 115 // 116 // This is where we put our bugs! 117 118 const domain = require('domain'); 119 120 // See the cluster documentation for more details about using 121 // worker processes to serve requests. How it works, caveats, etc. 122 123 const server = require('http').createServer((req, res) => { 124 const d = domain.create(); 125 d.on('error', (er) => { 126 console.error(`error ${er.stack}`); 127 128 // We're in dangerous territory! 129 // By definition, something unexpected occurred, 130 // which we probably didn't want. 131 // Anything can happen now! Be very careful! 132 133 try { 134 // Make sure we close down within 30 seconds 135 const killtimer = setTimeout(() => { 136 process.exit(1); 137 }, 30000); 138 // But don't keep the process open just for that! 139 killtimer.unref(); 140 141 // Stop taking new requests. 142 server.close(); 143 144 // Let the master know we're dead. This will trigger a 145 // 'disconnect' in the cluster master, and then it will fork 146 // a new worker. 147 cluster.worker.disconnect(); 148 149 // Try to send an error to the request that triggered the problem 150 res.statusCode = 500; 151 res.setHeader('content-type', 'text/plain'); 152 res.end('Oops, there was a problem!\n'); 153 } catch (er2) { 154 // Oh well, not much we can do at this point. 155 console.error(`Error sending 500! ${er2.stack}`); 156 } 157 }); 158 159 // Because req and res were created before this domain existed, 160 // we need to explicitly add them. 161 // See the explanation of implicit vs explicit binding below. 162 d.add(req); 163 d.add(res); 164 165 // Now run the handler function in the domain. 166 d.run(() => { 167 handleRequest(req, res); 168 }); 169 }); 170 server.listen(PORT); 171} 172 173// This part is not important. Just an example routing thing. 174// Put fancy application logic here. 175function handleRequest(req, res) { 176 switch (req.url) { 177 case '/error': 178 // We do some async stuff, and then... 179 setTimeout(() => { 180 // Whoops! 181 flerb.bark(); 182 }, timeout); 183 break; 184 default: 185 res.end('ok'); 186 } 187} 188``` 189 190## Additions to `Error` objects 191 192<!-- type=misc --> 193 194Any time an `Error` object is routed through a domain, a few extra fields 195are added to it. 196 197* `error.domain` The domain that first handled the error. 198* `error.domainEmitter` The event emitter that emitted an `'error'` event 199 with the error object. 200* `error.domainBound` The callback function which was bound to the 201 domain, and passed an error as its first argument. 202* `error.domainThrown` A boolean indicating whether the error was 203 thrown, emitted, or passed to a bound callback function. 204 205## Implicit binding 206 207<!--type=misc--> 208 209If domains are in use, then all **new** `EventEmitter` objects (including 210Stream objects, requests, responses, etc.) will be implicitly bound to 211the active domain at the time of their creation. 212 213Additionally, callbacks passed to lowlevel event loop requests (such as 214to `fs.open()`, or other callback-taking methods) will automatically be 215bound to the active domain. If they throw, then the domain will catch 216the error. 217 218In order to prevent excessive memory usage, `Domain` objects themselves 219are not implicitly added as children of the active domain. If they 220were, then it would be too easy to prevent request and response objects 221from being properly garbage collected. 222 223To nest `Domain` objects as children of a parent `Domain` they must be 224explicitly added. 225 226Implicit binding routes thrown errors and `'error'` events to the 227`Domain`'s `'error'` event, but does not register the `EventEmitter` on the 228`Domain`. 229Implicit binding only takes care of thrown errors and `'error'` events. 230 231## Explicit binding 232 233<!--type=misc--> 234 235Sometimes, the domain in use is not the one that ought to be used for a 236specific event emitter. Or, the event emitter could have been created 237in the context of one domain, but ought to instead be bound to some 238other domain. 239 240For example, there could be one domain in use for an HTTP server, but 241perhaps we would like to have a separate domain to use for each request. 242 243That is possible via explicit binding. 244 245```js 246// Create a top-level domain for the server 247const domain = require('domain'); 248const http = require('http'); 249const serverDomain = domain.create(); 250 251serverDomain.run(() => { 252 // Server is created in the scope of serverDomain 253 http.createServer((req, res) => { 254 // Req and res are also created in the scope of serverDomain 255 // however, we'd prefer to have a separate domain for each request. 256 // create it first thing, and add req and res to it. 257 const reqd = domain.create(); 258 reqd.add(req); 259 reqd.add(res); 260 reqd.on('error', (er) => { 261 console.error('Error', er, req.url); 262 try { 263 res.writeHead(500); 264 res.end('Error occurred, sorry.'); 265 } catch (er2) { 266 console.error('Error sending 500', er2, req.url); 267 } 268 }); 269 }).listen(1337); 270}); 271``` 272 273## `domain.create()` 274 275* Returns: {Domain} 276 277## Class: `Domain` 278 279* Extends: {EventEmitter} 280 281The `Domain` class encapsulates the functionality of routing errors and 282uncaught exceptions to the active `Domain` object. 283 284To handle the errors that it catches, listen to its `'error'` event. 285 286### `domain.members` 287 288* {Array} 289 290An array of timers and event emitters that have been explicitly added 291to the domain. 292 293### `domain.add(emitter)` 294 295* `emitter` {EventEmitter|Timer} emitter or timer to be added to the domain 296 297Explicitly adds an emitter to the domain. If any event handlers called by 298the emitter throw an error, or if the emitter emits an `'error'` event, it 299will be routed to the domain's `'error'` event, just like with implicit 300binding. 301 302This also works with timers that are returned from [`setInterval()`][] and 303[`setTimeout()`][]. If their callback function throws, it will be caught by 304the domain `'error'` handler. 305 306If the Timer or `EventEmitter` was already bound to a domain, it is removed 307from that one, and bound to this one instead. 308 309### `domain.bind(callback)` 310 311* `callback` {Function} The callback function 312* Returns: {Function} The bound function 313 314The returned function will be a wrapper around the supplied callback 315function. When the returned function is called, any errors that are 316thrown will be routed to the domain's `'error'` event. 317 318```js 319const d = domain.create(); 320 321function readSomeFile(filename, cb) { 322 fs.readFile(filename, 'utf8', d.bind((er, data) => { 323 // If this throws, it will also be passed to the domain. 324 return cb(er, data ? JSON.parse(data) : null); 325 })); 326} 327 328d.on('error', (er) => { 329 // An error occurred somewhere. If we throw it now, it will crash the program 330 // with the normal line number and stack message. 331}); 332``` 333 334### `domain.enter()` 335 336The `enter()` method is plumbing used by the `run()`, `bind()`, and 337`intercept()` methods to set the active domain. It sets `domain.active` and 338`process.domain` to the domain, and implicitly pushes the domain onto the domain 339stack managed by the domain module (see [`domain.exit()`][] for details on the 340domain stack). The call to `enter()` delimits the beginning of a chain of 341asynchronous calls and I/O operations bound to a domain. 342 343Calling `enter()` changes only the active domain, and does not alter the domain 344itself. `enter()` and `exit()` can be called an arbitrary number of times on a 345single domain. 346 347### `domain.exit()` 348 349The `exit()` method exits the current domain, popping it off the domain stack. 350Any time execution is going to switch to the context of a different chain of 351asynchronous calls, it's important to ensure that the current domain is exited. 352The call to `exit()` delimits either the end of or an interruption to the chain 353of asynchronous calls and I/O operations bound to a domain. 354 355If there are multiple, nested domains bound to the current execution context, 356`exit()` will exit any domains nested within this domain. 357 358Calling `exit()` changes only the active domain, and does not alter the domain 359itself. `enter()` and `exit()` can be called an arbitrary number of times on a 360single domain. 361 362### `domain.intercept(callback)` 363 364* `callback` {Function} The callback function 365* Returns: {Function} The intercepted function 366 367This method is almost identical to [`domain.bind(callback)`][]. However, in 368addition to catching thrown errors, it will also intercept [`Error`][] 369objects sent as the first argument to the function. 370 371In this way, the common `if (err) return callback(err);` pattern can be replaced 372with a single error handler in a single place. 373 374```js 375const d = domain.create(); 376 377function readSomeFile(filename, cb) { 378 fs.readFile(filename, 'utf8', d.intercept((data) => { 379 // Note, the first argument is never passed to the 380 // callback since it is assumed to be the 'Error' argument 381 // and thus intercepted by the domain. 382 383 // If this throws, it will also be passed to the domain 384 // so the error-handling logic can be moved to the 'error' 385 // event on the domain instead of being repeated throughout 386 // the program. 387 return cb(null, JSON.parse(data)); 388 })); 389} 390 391d.on('error', (er) => { 392 // An error occurred somewhere. If we throw it now, it will crash the program 393 // with the normal line number and stack message. 394}); 395``` 396 397### `domain.remove(emitter)` 398 399* `emitter` {EventEmitter|Timer} emitter or timer to be removed from the domain 400 401The opposite of [`domain.add(emitter)`][]. Removes domain handling from the 402specified emitter. 403 404### `domain.run(fn[, ...args])` 405 406* `fn` {Function} 407* `...args` {any} 408 409Run the supplied function in the context of the domain, implicitly 410binding all event emitters, timers, and lowlevel requests that are 411created in that context. Optionally, arguments can be passed to 412the function. 413 414This is the most basic way to use a domain. 415 416```js 417const domain = require('domain'); 418const fs = require('fs'); 419const d = domain.create(); 420d.on('error', (er) => { 421 console.error('Caught error!', er); 422}); 423d.run(() => { 424 process.nextTick(() => { 425 setTimeout(() => { // Simulating some various async stuff 426 fs.open('non-existent file', 'r', (er, fd) => { 427 if (er) throw er; 428 // proceed... 429 }); 430 }, 100); 431 }); 432}); 433``` 434 435In this example, the `d.on('error')` handler will be triggered, rather 436than crashing the program. 437 438## Domains and promises 439 440As of Node.js 8.0.0, the handlers of promises are run inside the domain in 441which the call to `.then()` or `.catch()` itself was made: 442 443```js 444const d1 = domain.create(); 445const d2 = domain.create(); 446 447let p; 448d1.run(() => { 449 p = Promise.resolve(42); 450}); 451 452d2.run(() => { 453 p.then((v) => { 454 // running in d2 455 }); 456}); 457``` 458 459A callback may be bound to a specific domain using [`domain.bind(callback)`][]: 460 461```js 462const d1 = domain.create(); 463const d2 = domain.create(); 464 465let p; 466d1.run(() => { 467 p = Promise.resolve(42); 468}); 469 470d2.run(() => { 471 p.then(p.domain.bind((v) => { 472 // running in d1 473 })); 474}); 475``` 476 477Domains will not interfere with the error handling mechanisms for 478promises. In other words, no `'error'` event will be emitted for unhandled 479`Promise` rejections. 480 481[`Error`]: errors.html#errors_class_error 482[`domain.add(emitter)`]: #domain_domain_add_emitter 483[`domain.bind(callback)`]: #domain_domain_bind_callback 484[`domain.exit()`]: #domain_domain_exit 485[`setInterval()`]: timers.html#timers_setinterval_callback_delay_args 486[`setTimeout()`]: timers.html#timers_settimeout_callback_delay_args 487[`throw`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/throw 488