Skip to content

Focus on What Hurts Most: AsyncContext for await as a First Step #124

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
dfahlander opened this issue May 20, 2025 · 1 comment
Open

Comments

@dfahlander
Copy link

dfahlander commented May 20, 2025

Suggestion for Narrowing Scope of AsyncContext to Its Core Use Case (for TC39 108th Meeting)

In light of @andreubotella’s upcoming presentation “How to simplify the web integration”, I’d like to respectfully propose narrowing the initial scope of the AsyncContext proposal to the most critical and unpolyfillable parts of async context propagation — namely async/await and Promises (and possibly async generators).

I raised a related issue earlier (#65) and, while it received some pushback at the time, I’d like to reintroduce the idea for further consideration. My intention is to help ensure this proposal delivers maximum impact with minimal complexity.

A Personal Stake: Dexie.js and Its Limitations

As the author of Dexie.js — a widely used IndexedDB wrapper featured in apps such as ChatGPT, WhatsApp Web, and Facebook Messenger — I deeply appreciate this proposal. Dexie uses its own internal async context to manage transactions and live queries. However, due to the absence of a standard, Dexie cannot interoperate with libraries like OpenTelemetry or Zone.js, which rely on their own workarounds.

This incompatibility directly stems from the lack of a consistent, standard way to track async context across await boundaries. Dexie and many other libraries don’t need the full machinery of async context for every possible async API — just async/await and Promises.


Focus the Proposal on What Cannot Be Polyfilled

A minimal version of AsyncContext — one that only targets native Promises and await — would provide immense value and interoperability across libraries. It would also:

  • Accelerate browser vendor adoption
  • Reduce cognitive overhead for developers
  • Preserve room for future opt-in complexity (e.g., setTimeout, EventTarget, etc.)

Libraries like Zone.js have already demonstrated that most web APIs can be polyfilled effectively — except await. That one gap is what this proposal is uniquely positioned to fill.


What Would React Do?

The React section in FRAMEWORKS.md points out that startTransition() loses context only across await. If this proposal focused solely on Promises and await, React’s issue could be addressed without introducing additional complexity. Limitations (e.g. lack of support for timers or event listeners) could be clearly documented for now.


What About Solid, Svelte, and Vue?

As reflected in FRAMEWORKS.md, these frameworks primarily care about preserving context across await. Supporting that alone would already unblock many current use cases.


What About OpenTelemetry?

OpenTelemetry for web depends on Zone.js, which successfully polyfills all but one async behavior: await. This single missing link means users must transpile all their async functions — a fragile and complex workaround.

By standardizing async context for await, we can eliminate this barrier and provide out-of-the-box support for libraries like OpenTelemetry in the browser — a major win for both observability and DX.


Can Implicit Context Propagation Be Added Later?

Yes, the proposal’s current form already leaves the door open to future extensions. For example, AsyncVariableOptions could be extended in the future with an additional option such as captureLevel, enabling opt-in deep propagation when desired.

This path allows TC39 to ship a minimal core that solves the hardest problem now, while keeping future complexity opt-in and backwards-compatible.


Conclusion

A narrowly scoped AsyncContext — focused on await and Promises — would already unlock massive value for libraries, frameworks, and developers, while simplifying both implementation and mental models.

Thank you for the ongoing work on this proposal. I’m happy to elaborate further or assist in any way that’s helpful!

@andreubotella
Copy link
Member

andreubotella commented May 20, 2025

Focus the Proposal on What Cannot Be Polyfilled

A minimal version of AsyncContext — one that only targets native Promises and await — would provide immense value and interoperability across libraries. It would also:

* Accelerate browser vendor adoption

* Reduce cognitive overhead for developers

* Preserve room for future opt-in complexity (e.g., `setTimeout`, `EventTarget`, etc.)

Libraries like Zone.js have already demonstrated that most web APIs can be polyfilled effectively — except await. That one gap is what this proposal is uniquely positioned to fill.

For what it's worth, some of the aspects of the current web integration proposal would be very hard to polyfill. For example, if you do window.onmessage = () => { /* ... */ }, the context in which that callback will be run depends on the context that called window.postMessage().

I believe the way Zone.js polyfills that is by patching everything so that the actual message sent contains a context ID, and by patching EventTarget and the various event handlers so that calling addEventListener or setting onmessage instead passes through a Zone.js hook that reads that message and sets the right context. This would also mean the Zone.js-equivalent library would need to keep an ID -> context map for all contexts that are passed to some similar web API, which for some APIs (although not for this one, I believe) could lead to memory leaks if there's a chance the equivalent to the message event won't be fired.

I believe there might also be cases where there really would be no way to polyfill the context accurately in userland, but I don't have any examples of that right now.


Can Implicit Context Propagation Be Added Later?

Yes, the proposal’s current form already leaves the door open to future extensions. For example, AsyncVariableOptions could be extended in the future with an additional option such as captureLevel, enabling opt-in deep propagation when desired.

This path allows TC39 to ship a minimal core that solves the hardest problem now, while keeping future complexity opt-in and backwards-compatible.

The main problem we're having with the web integration part of this proposal isn't that it needs to be done together with the await part of the proposal. It's that 1) there's a lot of complexity in figuring out how everything should behave, and 2) there's potentially a lot of complexity in implementing all of it, which Mozilla doesn't think is worth it. (To be fair, there might be more motivation for that if Mozilla sees the need after the await part is shipped, but considering everything, I doubt it.)

Even if we add at some later point a deep propagation flag, the problems of figuring out how everything should behave, and the complexity of implementing it all at once don't go away. Even if we add deep propagation later on with such a flag, the entirety of the deep propagation would need to be added at once. Otherwise, rather than a flag, you'd need to have something like a version level, which would need to be increased with every single incremental update to how the web integration works.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants