tc39 / proposal-explicit-resource-management

ECMAScript Explicit Resource Management

Home Page:https://arai-a.github.io/ecma262-compare/?pr=3000

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[Question] about `await using`

CGQAQ opened this issue · comments

commented
// an asynchronously-disposed, block-scoped resource
await using x = expr1;            // resource w/ local binding
await using y = expr2, z = expr4; // multiple resources
  1. What is the await awaiting for? The @@asyncDispose is executed when we go out of scope, isn't it? This seems changed the original behavior of the await keyword
  2. Can I catch the error thrown in @@asyncDispose by wrapping the await using line in the try-catch
  3. If 2 is not correct, x's @@asyncDispose throws an exception that will result y resource leak, how to avoid that?
  • What is the await awaiting for? The @@asyncDispose is executed when we go out of scope, isn't it? This seems changed the original behavior of the await keyword

The await is an indicator that there is an implicit await that will occur at the end of the block when the @@asyncDispose method is invoked. This is why the await indicator is tied to the using declaration itself, much like the await in for await indicates one or more implicit awaits will be performed as part of the evaluation of for.

This is consistent with for await in that for await (const x of y) does not await the value of y, but instead awaits certain steps within the AsyncIterator protocol.

  • Can I catch the error thrown in @@asyncDispose by wrapping the await using line in the try-catch

Yes, though if an exception is thrown both from later statements in the block and from dispose, or from multiple disposals, the exception you will catch may be a SuppressedError exception. This is intended to be an improvement over the try..finally statements that using/await using would replace:

try {
  const x = ...;
  try {
    ...
    throw new Error("A");
  } finally {
    await x.close(); 
  }
} catch (e) {
  // if `await x.close()` throws then `new Error("A")` is lost
}

vs

try {
  using x = ...;
  ...
  throw new Error("A");
} catch (e) {
  // if `await x[Symbol.asyncDispose]()` throws then you get a `SuppressdError` whose
  // `error` property is the exception from disposal and whose `suppressed` property is 
  // the error produced by `new Error("A")`, so neither exception is lost.
}
  • If 2 is not correct, x's @@asyncDispose throws an exception that will result y resource leak, how to avoid that?

All resources and their cleanup steps are pushed onto a stack associated with the current lexical environment. When the block scope exits for any reason (i.e., due to throw, return, break, continue, or normal program execution continuing past the end of the block), all resources in the stack are disposed in reverse order. For an await using that means that each resource is disposed after the previous disposal is await-ed, even if the previous disposal results in an exception. In general, no resources added with using or await using should leak, aside from cases where execution is suspended and is never resumed (such as an unclosed generator or an async function waiting on a forever-pending Promise).

commented

Oh! thanks!

The await is an indicator that there is an implicit await that will occur at the end of the block when the @@asyncDispose method is invoked.

@rbuckton I hope I am not wasting your time with this reply as I don't have the high level knowledge as you guys do, but just wanted to express my 2 cents from the point of view of your regular Joe developer. This kind of change breaks existing norms and it seems counter-intuitive. I am sure that there are technical justifications, won't object that, but usually when you are using await like this:

async function myFunction() {
   const foo = await someAsyncFunction();
   const bar = await someAsyncFunction();
}

you don't expect your program to reach bar until foo either fulfills or rejects to give the most basic example possible. await using kinda breaks the norm here and it's misleading since you're not really waiting for anything, which is how most people understand the await keyword.

All of this came to my attention from this video (link with relevant timestamp) on the proposal, so do give it a check if you like.

Have a great day/evening!