proposal-explicit-resource-management icon indicating copy to clipboard operation
proposal-explicit-resource-management copied to clipboard

[SUGGESTION]: static `run` method on DisposableStack / AsyncDisposableStack

Open lifaon74 opened this issue 9 months ago • 0 comments

I think, it could be a great addition to have a static run (not contractual name) method on DisposableStack / AsyncDisposableStack.

Definition:

class AsyncDisposableStack {
  // NOT SURE OF THE OPTIMAL IMPLEMENTATION

  static async run<GReturn>(
    context: (stack: AsyncDisposableStack) => Promise<GReturn> | GReturn,
  ): Promise<GReturn> {
    const stack: AsyncDisposableStack = new AsyncDisposableStack();

    let result: GReturn;

    try {
      result = await context(stack);
    } catch (error: unknown) {
      try {
        await stack.disposeAsync();
      } catch (disposeError: unknown) {
        throw new SuppressedError(disposeError, error);
      }
      throw error;
    }

    await stack.disposeAsync();

    return result;
  }

  // OR MAYBE

  static async run<GReturn>(
    context: (stack: AsyncDisposableStack) => Promise<GReturn> | GReturn,
  ): Promise<GReturn> {
    const stack: AsyncDisposableStack = new AsyncDisposableStack();

    try {
      return context(stack);
    } finally {
      await stack.disposeAsync();
    }
  }
  
  // ...
}

Example:

await AsyncDisposableStack.run(
  async (stack: AsyncDisposableStack): Promise<void> => {
    const client = stack.use(/* something AsyncDisposable */);

    stack.defer((): void => {
      // register something to dispose like an eventListener for example.
    });

    await client.send('message');

    // some other async code

    // => if an error is thrown, or the async function return, then the stack is properly disposed.
  },
);

What's your opinion ?

lifaon74 avatar Feb 25 '25 14:02 lifaon74