Skip to content

Commit c5fcc50

Browse files
committed
diagnostics_channel: return original thenable
This makes tracePromise return the original thenable to allow custom thenable types to retain their methods rather than producing the chained result type.
1 parent 2263b4d commit c5fcc50

File tree

2 files changed

+23
-2
lines changed

2 files changed

+23
-2
lines changed

lib/diagnostics_channel.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ const {
1010
ObjectDefineProperty,
1111
ObjectGetPrototypeOf,
1212
ObjectSetPrototypeOf,
13+
Promise,
14+
PromisePrototypeThen,
1315
ReflectApply,
1416
SafeFinalizationRegistry,
1517
SafeMap,
@@ -26,6 +28,7 @@ const {
2628
} = require('internal/validators');
2729

2830
const { triggerUncaughtException } = internalBinding('errors');
31+
const { isPromise } = require('internal/util/types');
2932

3033
const dc_binding = internalBinding('diagnostics_channel');
3134
const { subscribers: subscriberCounts } = dc_binding;
@@ -375,6 +378,10 @@ class TracingChannel {
375378
asyncStart.publish(context);
376379
// TODO: Is there a way to have asyncEnd _after_ the continuation?
377380
asyncEnd.publish(context);
381+
}
382+
383+
function rejectAndRethrow(err) {
384+
reject(err);
378385
throw err;
379386
}
380387

@@ -396,7 +403,15 @@ class TracingChannel {
396403
context.result = result;
397404
return result;
398405
}
399-
return result.then(resolve, reject);
406+
if (isPromise(result) && result.constructor === Promise) {
407+
return PromisePrototypeThen(result, resolve, rejectAndRethrow);
408+
}
409+
// For non-native thenables, subscribe to the result but return the
410+
// original thenable so the consumer can continue handling it directly.
411+
// Non-native thenables don't have unhandledRejection tracking, so
412+
// swallowing the rejection here doesn't change existing behaviour.
413+
result.then(resolve, reject);
414+
return result;
400415
} catch (err) {
401416
context.error = err;
402417
error.publish(context);

test/parallel/test-diagnostics-channel-tracing-channel-promise-thenable.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ class ResolvedThenable {
1212
then(resolve) {
1313
return new ResolvedThenable(resolve(this.#result));
1414
}
15+
customMethod() {
16+
return this.#result;
17+
}
1518
}
1619

1720
const channel = dc.tracingChannel('test');
@@ -49,7 +52,10 @@ const result = channel.tracePromise(common.mustCall(function(value) {
4952
}), input, thisArg, expectedResult);
5053

5154
assert(result instanceof ResolvedThenable);
52-
assert.notStrictEqual(result, innerThenable);
55+
// With branching then, the original thenable is returned directly so that
56+
// extra methods defined on it remain accessible to the caller.
57+
assert.strictEqual(result, innerThenable);
58+
assert.deepStrictEqual(result.customMethod(), expectedResult);
5359
result.then(common.mustCall((value) => {
5460
assert.deepStrictEqual(value, expectedResult);
5561
}));

0 commit comments

Comments
 (0)