🌊 Turns an iterable, async iterable or iterator into a stream.

Info

The inverse of streamToIter, and the step that hands an *Iter pipeline to anything that consumes a ReadableStreamResponse, fetch, pipeThrough.

Syntax

TypeScript
import { iterToStream } from '@opentf/std';

iterToStream<T>(
  iter: AsyncIterable<T> | Iterable<T> | { next: () => Promise<IteratorResult<T>> | IteratorResult<T> },
): ReadableStream<T>;

Parameters

NameTypeDescription
iterAsyncIterable<T> | Iterable<T> | Iterator<T>The source to read.

Returns

A ReadableStream<T> over every value, in order.

Anything not iterable throws a TypeError at the call, rather than on the first read.

Examples

Serving a generator as a response body:

TypeScript
async function* rows() {
  for await (const row of query()) {
    yield JSON.stringify(row) + '\n';
  }
}

new Response(iterToStream(rows()));

Round tripping a stream through the Iter operators:

TypeScript
import { streamToIter, iterToStream, mapIterAsync } from '@opentf/std';
const upper = mapIterAsync(streamToIter(input), (chunk) => chunk.toUpperCase());

const output = iterToStream(upper);

Backpressure

The source is pulled lazily, one value per read, so nothing is produced until the consumer asks. An infinite generator is safe:

TypeScript
function* naturals() {
  let n = 0;
  while (true) yield n++;
}

const reader = iterToStream(naturals()).getReader();

await reader.read(); //=> { value: 0, done: false }
await reader.cancel(); // the generator's `finally` runs here

Cancelling the stream calls return on the source, so a generator's finally block runs and its resources are released.

Last updated on
Edit this page