Converting between iterables and web streams


It’s now easy to turn an iterator into a readable stream and a readable stream into an array. Let’s take a look.

ReadableStream.from()

ReadableStream.from is currently supported in Firefox Nightly and Deno.

ReadableStream.from() takes any sync or async iterable and returns a readable stream. The text of the PR to the spec reads:

This static method takes an async iterable and returns a ReadableStream pulling chunks from that async iterable. Sync iterables (including arrays and generators) are also supported, since GetIterator() already has all the necessary handling to adapt a sync iterator into an async iterator.


const syncIterable = ['hello', 'world'];
const stream1 = ReadableStream.from(syncIterable);

async function* createAsyncIterable() {
  yield 'foo';
  yield 'bar';
  yield 'baz';
}
const stream2 = ReadableStream.from(createAsyncIterable());

Array.fromAsync()

Array.fromAsync is currently supported in Safari 16.4, Firefox 115 and Bun. Caveat: Chrome and Safari do not treat streams as async iterables yet, so Array.fromAsync() won’t work on streams in those browsers.

We already had Array.from. Array.fromAsync is the same but works with both synchronous and asynchronous iterables. fromAsync() returns a promise that resolves with an array.

Readable streams are asynchronously iterable, so we can use fromAsync() to turn a readable stream into an array. Each chunk of the stream becomes an array item.

It’s not a practical real-world example, but to keep things simple lets revisit the stream we created earlier:

async function* createAsyncIterable() {
  yield 'foo';
  yield 'bar';
  yield 'baz';
}
const stream = ReadableStream.from(createAsyncIterable());
const array = await Array.fromAsync(stream);
console.log(array); // [ "foo", "bar", "baz" ]