Array.fromAsync()This blog post is about the ECMAScript proposal “Array.fromAsync for JavaScript” by J. S. Choi. It introduces a static method for converting asynchronous iterables to Arrays.
Currently JavaScript provides several tools for working with synchronous iterables – for example:
function* createSyncIterable() {
yield 'a';
yield 'b';
yield 'c';
}
// Array-destructuring
const [elem0, elem1] = createSyncIterable();
assert.equal(elem0, 'a');
assert.equal(elem1, 'b');
// Spreading into Arrays
assert.deepEqual(
['>', ...createSyncIterable(), '<'],
['>', 'a', 'b', 'c', '<']
);
// Spreading into function arguments
const arr = [];
arr.push('>', ...createSyncIterable(), '<');
assert.deepEqual(
arr,
['>', 'a', 'b', 'c', '<']
);
// Array.from()
assert.deepEqual(
Array.from(createSyncIterable()),
['a', 'b', 'c']
);
assert.deepEqual(
Array.from(createSyncIterable(), s => s + s),
['aa', 'bb', 'cc']
);
// for-of loop
for (const x of createSyncIterable()) {
console.log(x);
}
// Output:
// a
// b
// c
For working with asynchronous iterables, we currently only have the for-await-of loop.
Array.fromAsync() Array.fromAsync() is the asynchronous version of Array.from():
interface ArrayConstructor {
fromAsync<T>(
asyncIterable: AsyncIterable<T>
): Promise<Array<T>>;
fromAsync<T, U>(
asyncIterable: AsyncIterable<T>,
mapFn: (value: T, index: number) => U,
thisArg?: any
): Promise<Array<U>>;
// ···
}
Array.fromAsync() accepts up to three arguments:
asyncIterable is the asynchronous iterable that is converted to an Array.mapFn lets us transform the iterated values before they are added to the Array that is returned. If we provide this argument, Array.fromAsync() works similarly to the Array method .map().thisArg lets us specify the value of this for mapFn.Given that the values in asyncIterable can’t be collected synchronously, Array.fromAsync() works asynchronously and returns a Promise for an Array. Therefore, we await its results in the following example:
async function* createAsyncIterable() {
yield 1;
yield 2;
yield 3;
}
assert.deepEqual(
await Array.fromAsync(createAsyncIterable()),
[1, 2, 3]
);
assert.deepEqual(
await Array.fromAsync(createAsyncIterable(), x => x * x),
[1, 4, 9]
);
Array.fromAsync() We could implement Array.fromAsync() like this:
async function arrayFromAsync(
asyncIterable, mapFn=x=>x, thisArg=undefined
) {
const result = [];
for await (const elem of asyncIterable) {
result.push(mapFn.call(thisArg, elem));
}
return result;
}
In Node.js, readable web streams are asynchronously iterable. Therefore, we can use Array.fromAsync() to collect all the chunks (pieces of data) of a ReadableStream in an Array.
We’ll read the following file data.txt:
First line
Second line
This is the code:
import {Readable} from 'node:stream';
import * as fs from 'node:fs';
const readableStream = Readable.toWeb(
fs.createReadStream('data.txt', 'utf-8')
);
const chunks = await Array.fromAsync(readableStream);
assert.deepEqual(
chunks,
['First line\nSecond line']
);
Array.from()” in “Exploring JavaScript”