v134 · javascript

Streaming pipeline

AsyncDisposableStack manages a multi-stage streaming pipeline: HTTP reader → decoder → transformer → writer. If any stage throws, the stack unwinds in LIFO order — every open stream is closed even if earlier cleanup fails. No try/finally nesting required.

Symbol.asyncDispose: checking…
Streaming pipeline stages idle
HTTP Reader
📥
idle
Decoder
🔤
idle
Transformer
idle
Writer
💾
idle
Scenarios
Execution log
Choose a scenario to run…
// AsyncDisposableStack manages a 4-stage streaming pipeline.
// If any stage throws, all earlier stages are disposed in LIFO order.

async function runPipeline(scenario) {
  await using stack = new AsyncDisposableStack();

  // Acquire each stage — add to stack immediately so it's cleaned up on throw
  const reader = await openHttpReader(url);
  stack.defer(async () => { await reader.cancel(); log('Reader cancelled'); });

  const decoder = new TextDecoderStream();
  stack.defer(async () => { await decoder.readable.cancel(); log('Decoder closed'); });

  const transformer = new TransformStream({ ... });
  stack.defer(async () => { await transformer.readable.cancel(); log('Transformer closed'); });

  const writer = await openFileWriter(path);
  stack.defer(async () => { await writer.close(); log('Writer closed'); });

  // Pipeline is open; process stream
  for await (const chunk of reader.pipeThrough(decoder).pipeThrough(transformer)) {
    await writer.write(chunk);
    if (scenario === 'throw-transform') throw new Error('transformer failed');
  }

  // Happy path: stack disposes all stages in LIFO order when block exits
}

// On throw, stack unwinds: Writer → Transformer → Decoder → Reader
// All disposers run even if an earlier one throws

see also

references