• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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