Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 84dabe8

Browse filesBrowse files
committed
domain: support promises
Fixes: #10724 PR-URL: #12489 Reviewed-By: Matthew Loring <mattloring@google.com> Reviewed-By: Julien Gilli <jgilli@nodejs.org> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
1 parent e5a25cb commit 84dabe8
Copy full SHA for 84dabe8

File tree

Expand file treeCollapse file tree

3 files changed

+234
-0
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

3 files changed

+234
-0
lines changed
Open diff view settings
Collapse file

‎doc/api/domain.md‎

Copy file name to clipboardExpand all lines: doc/api/domain.md
+50Lines changed: 50 additions & 0 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
11
# Domain
2+
<!-- YAML
3+
changes:
4+
- version: REPLACEME
5+
pr-url: https://github.com/nodejs/node/pull/12489
6+
description: Handlers for `Promise`s are now invoked in the domain in which
7+
the first promise of a chain was created.
8+
-->
29

310
> Stability: 0 - Deprecated
411
@@ -444,6 +451,49 @@ d.run(() => {
444451
In this example, the `d.on('error')` handler will be triggered, rather
445452
than crashing the program.
446453

454+
## Domains and Promises
455+
456+
As of Node REPLACEME, the handlers of Promises are run inside the domain in
457+
which the call to `.then` or `.catch` itself was made:
458+
459+
```js
460+
const d1 = domain.create();
461+
const d2 = domain.create();
462+
463+
let p;
464+
d1.run(() => {
465+
p = Promise.resolve(42);
466+
});
467+
468+
d2.run(() => {
469+
p.then((v) => {
470+
// running in d2
471+
});
472+
});
473+
```
474+
475+
A callback may be bound to a specific domain using [`domain.bind(callback)`][]:
476+
477+
```js
478+
const d1 = domain.create();
479+
const d2 = domain.create();
480+
481+
let p;
482+
d1.run(() => {
483+
p = Promise.resolve(42);
484+
});
485+
486+
d2.run(() => {
487+
p.then(p.domain.bind((v) => {
488+
// running in d1
489+
}));
490+
});
491+
```
492+
493+
Note that domains will not interfere with the error handling mechanisms for
494+
Promises, i.e. no `error` event will be emitted for unhandled Promise
495+
rejections.
496+
447497
[`domain.add(emitter)`]: #domain_domain_add_emitter
448498
[`domain.bind(callback)`]: #domain_domain_bind_callback
449499
[`domain.dispose()`]: #domain_domain_dispose
Collapse file

‎src/node.cc‎

Copy file name to clipboardExpand all lines: src/node.cc
+56Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ using v8::Number;
143143
using v8::Object;
144144
using v8::ObjectTemplate;
145145
using v8::Promise;
146+
using v8::PromiseHookType;
146147
using v8::PromiseRejectMessage;
147148
using v8::PropertyCallbackInfo;
148149
using v8::ScriptOrigin;
@@ -1114,6 +1115,58 @@ bool ShouldAbortOnUncaughtException(Isolate* isolate) {
11141115
}
11151116

11161117

1118+
void DomainPromiseHook(PromiseHookType type,
1119+
Local<Promise> promise,
1120+
Local<Value> parent,
1121+
void* arg) {
1122+
Environment* env = static_cast<Environment*>(arg);
1123+
Local<Context> context = env->context();
1124+
1125+
if (type == PromiseHookType::kResolve) return;
1126+
if (type == PromiseHookType::kInit && env->in_domain()) {
1127+
promise->Set(context,
1128+
env->domain_string(),
1129+
env->domain_array()->Get(context,
1130+
0).ToLocalChecked()).FromJust();
1131+
return;
1132+
}
1133+
1134+
// Loosely based on node::MakeCallback().
1135+
Local<Value> domain_v =
1136+
promise->Get(context, env->domain_string()).ToLocalChecked();
1137+
if (!domain_v->IsObject())
1138+
return;
1139+
1140+
Local<Object> domain = domain_v.As<Object>();
1141+
if (domain->Get(context, env->disposed_string())
1142+
.ToLocalChecked()->IsTrue()) {
1143+
return;
1144+
}
1145+
1146+
if (type == PromiseHookType::kBefore) {
1147+
Local<Value> enter_v =
1148+
domain->Get(context, env->enter_string()).ToLocalChecked();
1149+
if (enter_v->IsFunction()) {
1150+
if (enter_v.As<Function>()->Call(context, domain, 0, nullptr).IsEmpty()) {
1151+
FatalError("node::PromiseHook",
1152+
"domain enter callback threw, please report this "
1153+
"as a bug in Node.js");
1154+
}
1155+
}
1156+
} else {
1157+
Local<Value> exit_v =
1158+
domain->Get(context, env->exit_string()).ToLocalChecked();
1159+
if (exit_v->IsFunction()) {
1160+
if (exit_v.As<Function>()->Call(context, domain, 0, nullptr).IsEmpty()) {
1161+
FatalError("node::MakeCallback",
1162+
"domain exit callback threw, please report this "
1163+
"as a bug in Node.js");
1164+
}
1165+
}
1166+
}
1167+
}
1168+
1169+
11171170
void SetupDomainUse(const FunctionCallbackInfo<Value>& args) {
11181171
Environment* env = Environment::GetCurrent(args);
11191172

@@ -1153,9 +1206,12 @@ void SetupDomainUse(const FunctionCallbackInfo<Value>& args) {
11531206
Local<ArrayBuffer> array_buffer =
11541207
ArrayBuffer::New(env->isolate(), fields, sizeof(*fields) * fields_count);
11551208

1209+
env->AddPromiseHook(DomainPromiseHook, static_cast<void*>(env));
1210+
11561211
args.GetReturnValue().Set(Uint32Array::New(array_buffer, 0, fields_count));
11571212
}
11581213

1214+
11591215
void RunMicrotasks(const FunctionCallbackInfo<Value>& args) {
11601216
args.GetIsolate()->RunMicrotasks();
11611217
}
Collapse file
+128Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
'use strict';
2+
const common = require('../common');
3+
const assert = require('assert');
4+
const domain = require('domain');
5+
const fs = require('fs');
6+
const vm = require('vm');
7+
8+
common.crashOnUnhandledRejection();
9+
10+
{
11+
const d = domain.create();
12+
13+
d.run(common.mustCall(() => {
14+
Promise.resolve().then(common.mustCall(() => {
15+
assert.strictEqual(process.domain, d);
16+
}));
17+
}));
18+
}
19+
20+
{
21+
const d = domain.create();
22+
23+
d.run(common.mustCall(() => {
24+
Promise.resolve().then(() => {}).then(() => {}).then(common.mustCall(() => {
25+
assert.strictEqual(process.domain, d);
26+
}));
27+
}));
28+
}
29+
30+
{
31+
const d = domain.create();
32+
33+
d.run(common.mustCall(() => {
34+
vm.runInNewContext(`Promise.resolve().then(common.mustCall(() => {
35+
assert.strictEqual(process.domain, d);
36+
}));`, { common, assert, process, d });
37+
}));
38+
}
39+
40+
{
41+
const d1 = domain.create();
42+
const d2 = domain.create();
43+
let p;
44+
d1.run(common.mustCall(() => {
45+
p = Promise.resolve(42);
46+
}));
47+
48+
d2.run(common.mustCall(() => {
49+
p.then(common.mustCall((v) => {
50+
assert.strictEqual(process.domain, d2);
51+
assert.strictEqual(p.domain, d1);
52+
}));
53+
}));
54+
}
55+
56+
{
57+
const d1 = domain.create();
58+
const d2 = domain.create();
59+
let p;
60+
d1.run(common.mustCall(() => {
61+
p = Promise.resolve(42);
62+
}));
63+
64+
d2.run(common.mustCall(() => {
65+
p.then(p.domain.bind(common.mustCall((v) => {
66+
assert.strictEqual(process.domain, d1);
67+
assert.strictEqual(p.domain, d1);
68+
})));
69+
}));
70+
}
71+
72+
{
73+
const d1 = domain.create();
74+
const d2 = domain.create();
75+
let p;
76+
d1.run(common.mustCall(() => {
77+
p = Promise.resolve(42);
78+
}));
79+
80+
d1.run(common.mustCall(() => {
81+
d2.run(common.mustCall(() => {
82+
p.then(common.mustCall((v) => {
83+
assert.strictEqual(process.domain, d2);
84+
assert.strictEqual(p.domain, d1);
85+
}));
86+
}));
87+
}));
88+
}
89+
90+
{
91+
const d1 = domain.create();
92+
const d2 = domain.create();
93+
let p;
94+
d1.run(common.mustCall(() => {
95+
p = Promise.reject(new Error('foobar'));
96+
}));
97+
98+
d2.run(common.mustCall(() => {
99+
p.catch(common.mustCall((v) => {
100+
assert.strictEqual(process.domain, d2);
101+
assert.strictEqual(p.domain, d1);
102+
}));
103+
}));
104+
}
105+
106+
{
107+
const d = domain.create();
108+
109+
d.run(common.mustCall(() => {
110+
Promise.resolve().then(common.mustCall(() => {
111+
setTimeout(common.mustCall(() => {
112+
assert.strictEqual(process.domain, d);
113+
}), 0);
114+
}));
115+
}));
116+
}
117+
118+
{
119+
const d = domain.create();
120+
121+
d.run(common.mustCall(() => {
122+
Promise.resolve().then(common.mustCall(() => {
123+
fs.readFile(__filename, common.mustCall(() => {
124+
assert.strictEqual(process.domain, d);
125+
}));
126+
}));
127+
}));
128+
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.