Skip to content
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

Asynchronously using a synchronous disposable that returns a Promise #256

Closed
rictic opened this issue Feb 21, 2025 · 6 comments
Closed

Asynchronously using a synchronous disposable that returns a Promise #256

rictic opened this issue Feb 21, 2025 · 6 comments

Comments

@rictic
Copy link

rictic commented Feb 21, 2025

Should a promise returned by a Symbol.dispose method be awaited by AsyncDisposableStack#use / await using?

Put another way:

{
  const stack = new AsyncDisposableStack();
  const neverResolves = Promise.withResolvers().promise;
  stack.use({[Symbol.dispose]() { return neverResolves }});
  await stack.disposeAsync();
  console.log('[1] does this line of code ever run?'); 
}
{
  {
    const neverResolves = Promise.withResolvers().promise;
    await using _ = {[Symbol.dispose]: () => neverResolves};
  }
  console.log('[2] does this line of code ever run?');
}
@rictic
Copy link
Author

rictic commented Feb 21, 2025

The native implementation in Chrome Canary Version 135.0.7026.0 (Official Build) canary (arm64) does not run either console.log statement. [Edit: Version 135.0.7036.0 runs both log statements.]

The TypeScript runtime emit for the second code snippet does log. TypeScript Playground Link

The AsyncDisposableStack polyfill in https://github.com/es-shims/DisposableStack does log for the first snippet.

The AsyncDisposableStack polyfill at https://github.com/nberlette/dispose does not log for the first snippet.

@rictic
Copy link
Author

rictic commented Feb 21, 2025

My read of the spec is that both should be logged, because of https://tc39.es/proposal-explicit-resource-management/#sec-getdisposemethod 1.b.ii which says that GetDisposeMethod, when called with an async-dispose hint, should return a newly created closure that synchronously returns a new, already-settled promise when it's called. That promise is rejected if the Symbol.dispose method throws, the promise is resolved to undefined.

So even though CreateDisposableResource when called with an async-dispose hint always creates a DisposableResource Record with an async hint, the DisposeMethod it's given isn't able to observe any promise that might be returned by a Symbol.dispose method.

@rbuckton
Copy link
Collaborator

Both should be logged. Per https://arai-a.github.io/ecma262-compare/?pr=3000&id=sec-getdisposemethod, the result of invoking the Symbol.dispose method is only used to reject the Promise.

@syg: Sounds like a bug in V8?

@syg
Copy link

syg commented Feb 21, 2025

cc @rmahdav

@syg
Copy link

syg commented Feb 21, 2025

Confirm there's a bug in V8 with some accidental promise chaining in the async-from-sync adapter.

@rmahdav
Copy link

rmahdav commented Feb 24, 2025

Thank you, Peter. A fix has landed for this bug.

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

4 participants