Skip to content

lib: add observePromise helper#62391

Draft
Qard wants to merge 1 commit intonodejs:mainfrom
Qard:observe-promise-without-handled-state
Draft

lib: add observePromise helper#62391
Qard wants to merge 1 commit intonodejs:mainfrom
Qard:observe-promise-without-handled-state

Conversation

@Qard
Copy link
Member

@Qard Qard commented Mar 22, 2026

I'm looking for feedback on this experiment. This is helpful to observe promise resolution without considering the promise as handled. This allows the unhandledRejection event to still be produced if no other resolution handling occurs. This includes a very small V8 change. Assuming people find the intent of this change reasonable, I would try to get that upstreamed to V8. I just don't want to put in all the effort to make that happen if the thing itself is not likely to be accepted.

The particular use case I have in mind for this is the tracePromise method in TracingChannel. Currently it wraps thenables in a Promise.resolve(...) and then calls the then(...) on it. This is problematic though as the thenable could have other methods which disappear as the return value becomes a native promise instead. Rather than using chaining there which produces a new and crucially different promise, I would like to return the original promise. The issue is that if we just branch internally for our observation and then return the original that original is considered handled and so would no longer trigger unhandledRejection if no other continuation was attached. This change would provide an interface such that we can observe that result but opt-out of the handled transition.

I imagine there might be other use cases for this in other parts of the codebase. This is intended purely as an internal utility, I don't expect we'd actually expose it in the public API anywhere.

What do folks think? Is this a reasonable solution we should be applying?

cc @nodejs/diagnostics @nodejs/v8

This is helpful to observe promise resolution without
considering the promise as handled. This allows the
unhandledRejection event to still be produced if no
other resolution handling occurs.
@Qard Qard self-assigned this Mar 22, 2026
@Qard Qard added discuss Issues opened for discussions and feedbacks. v8 engine Issues and PRs related to the V8 dependency. lib / src Issues and PRs related to general changes in the lib or src directory. promises Issues and PRs related to ECMAScript promises. diagnostics_channel Issues and PRs related to diagnostics channel labels Mar 22, 2026
@nodejs-github-bot
Copy link
Collaborator

Review requested:

  • @nodejs/loaders
  • @nodejs/security-wg
  • @nodejs/v8-update

@nodejs-github-bot nodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. needs-ci PRs that need a full CI run. labels Mar 22, 2026
@mcollina
Copy link
Member

What's the runtime overhead?

@Qard
Copy link
Member Author

Qard commented Mar 22, 2026

No idea. Haven't measured it yet. Just wanted to validate the approach seemed reasonable before pursuing it too far. I could always write an intrinsic for setting the handled marker tough, as I did for AsyncLocalStorage with the ContinuationPreservedEmbedderData interface, if the perf impact turns out to be enough to matter. Just a bit more work.

const err2 = new Error('test2');
const p2 = Promise.reject(err2);

observePromise(p2, null, common.mustCall(() => {}));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe assert on err2 like in test 1 and 3

@Flarna
Copy link
Member

Flarna commented Mar 22, 2026

I like the idea.
But I think it is actually useful for userland - like you wrote "APM and diagnostics tools can use this...".
If it is not expose they can't use it. Only via tracePromise once it is added there but I think this is an unneeded restriction.

@Qard
Copy link
Member Author

Qard commented Mar 22, 2026

I'm unclear what use case there would be to make it public though. If you have any ideas, I'd be happy to entertain the idea. My plan was to just try it out internally, see if it works okay, and we can always decide to make it public later if we're happy with it. A lot harder to go the other direction. 😅

@Flarna
Copy link
Member

Flarna commented Mar 23, 2026

The usecases are mostly the same as tracePromise in diagnostic channel.
tracePromise does not fit all possible cases so users/apm tools/... might want to implement a their own variant fitting to their concrete needed.

If this API is internal only it would imply that such very specific cases would require DC enhancements.

As you wrote in the comment "APM and diagnostics tools can use this" - but node.js itself is no APM nor a diagnostics tool - but there are quite a lot such tools using node.js as runtime.

But I agree, there is no need to make it public on first iteration.

@mcollina
Copy link
Member

I like the idea, with the overhead being the biggest blocker.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c++ Issues and PRs that require attention from people who are familiar with C++. diagnostics_channel Issues and PRs related to diagnostics channel discuss Issues opened for discussions and feedbacks. lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run. promises Issues and PRs related to ECMAScript promises. v8 engine Issues and PRs related to the V8 dependency.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants