| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent e5a25cb commit 84dabe8
3 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,4 +1,11 @@ | |||
| 1 | 1 | # 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 | + --> | ||
| 2 | 9 | ||
| 3 | 10 | > Stability: 0 - Deprecated | |
| 4 | 11 | ||
@@ -444,6 +451,49 @@ d.run(() => { | |||
| 444 | 451 | In this example, the `d.on('error')` handler will be triggered, rather | |
| 445 | 452 | than crashing the program. | |
| 446 | 453 | ||
| 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 | + | ||
| 447 | 497 | [`domain.add(emitter)`]: #domain_domain_add_emitter | |
| 448 | 498 | [`domain.bind(callback)`]: #domain_domain_bind_callback | |
| 449 | 499 | [`domain.dispose()`]: #domain_domain_dispose | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -143,6 +143,7 @@ using v8::Number; | |||
| 143 | 143 | using v8::Object; | |
| 144 | 144 | using v8::ObjectTemplate; | |
| 145 | 145 | using v8::Promise; | |
| 146 | + using v8::PromiseHookType; | ||
| 146 | 147 | using v8::PromiseRejectMessage; | |
| 147 | 148 | using v8::PropertyCallbackInfo; | |
| 148 | 149 | using v8::ScriptOrigin; | |
@@ -1114,6 +1115,58 @@ bool ShouldAbortOnUncaughtException(Isolate* isolate) { | |||
| 1114 | 1115 | } | |
| 1115 | 1116 | ||
| 1116 | 1117 | ||
| 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 | + | ||
| 1117 | 1170 | void SetupDomainUse(const FunctionCallbackInfo<Value>& args) { | |
| 1118 | 1171 | Environment* env = Environment::GetCurrent(args); | |
| 1119 | 1172 | ||
@@ -1153,9 +1206,12 @@ void SetupDomainUse(const FunctionCallbackInfo<Value>& args) { | |||
| 1153 | 1206 | Local<ArrayBuffer> array_buffer = | |
| 1154 | 1207 | ArrayBuffer::New(env->isolate(), fields, sizeof(*fields) * fields_count); | |
| 1155 | 1208 | ||
| 1209 | + env->AddPromiseHook(DomainPromiseHook, static_cast<void*>(env)); | ||
| 1210 | + | ||
| 1156 | 1211 | args.GetReturnValue().Set(Uint32Array::New(array_buffer, 0, fields_count)); | |
| 1157 | 1212 | } | |
| 1158 | 1213 | ||
| 1214 | + | ||
| 1159 | 1215 | void RunMicrotasks(const FunctionCallbackInfo<Value>& args) { | |
| 1160 | 1216 | args.GetIsolate()->RunMicrotasks(); | |
| 1161 | 1217 | } | |
| Original file line number | Diff line number | Diff 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 | + } | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments