demo · v134 · javascript

AsyncDisposableStack

Acquire N async resources conditionally, get LIFO async cleanup on success or throw. Run an instrumented async pipeline — temp file, auth token, row lock, queue message — make any step fail and watch the real AsyncDisposableStack unwind in reverse.

Each resource is async-acquired, registered with stack.use(), and async-disposed by the runtime. Pick which step should fail.

waiting…
async function run() {
  await using stack = new AsyncDisposableStack();

  const tmp   = stack.use(await open("/tmp/job"));
  const token = stack.use(await fetchToken());
  const lock  = stack.use(await db.lockRow(token, 42));
  const msg   = stack.use(await queue.publish({ tmp, lock }));

  return msg.id;
  // any throw above → stack.[Symbol.asyncDispose]()
  // runs in LIFO order, awaiting each .[Symbol.asyncDispose].
}

why this matters

Try/finally pyramids of awaits get ugly fast. Worse, error-handling around each finally has to repeat the rollback logic, and an awaited finally can swallow the original rejection (see SuppressedError on the sync demo for the same problem class). AsyncDisposableStack centralises the rollback as a stack of disposers — same shape as RAII in C++ or Python's contextlib.ExitStack.

see also