Array.fromAsyncsequential
0 mstotal = sum
await Array.fromAsync(
tasks, t => t()
);
// each awaits the previous
Promise.allparallel
0 mstotal = slowest
await Promise.all(
tasks.map(t => t())
);
// all fire at once
Promise.all can't do this. A paginated API modelled as an async generator doesn't know its length upfront.
Array.fromAsync walks it lazily, collecting every item across every page into one array.
async function* fetchAllPages(url) {
let next = url;
while (next) {
const page = await fetch(next).then(r => r.json());
for (const item of page.items) yield item;
next = page.nextPage; // null when done
}
}
const all = await Array.fromAsync(fetchAllPages('/api/products'));
idle — 0 items collected
// items appear here as each page arrives…
Two gotchas that bite in production. Sequential naturally throttles a rate-limited API; parallel hammers it. And
Array.fromAsync always returns a Promise — forget await and you get the wrong type.
Rate-limit simulator (API allows 2 concurrent)
// fire requests and watch for 429s…
The forgotten-await gotcha
❌ Missing await
Array.fromAsync(gen())
// —
✅ With await
await Array.fromAsync(gen())
// —