JavaScript has supported async iterators since ES2018. All modern browser versions handle async iterables and Node.js can do it since version 10.x. Async iterables have a positive aspect and a drawback at the same time: you need to consume them to know the result. That means you can’t check if an async iterable is empty until you start consuming it.
In some situations, it’s helpful to create a JavaScript array from an async iterable. And this tutorial shows you how to do it.
JavaScript Series Overview
Using Array.fromAsync
At the time of writing this tutorial, the browsers and JavaScript runtimes added support for a new, static JavaScript function: Array.fromAsync
. The Array.fromAsync
method accepts an iterable, async iterable, or an array-like object as a parameter and returns an array.
For example, a string in JavaScript is array-like and you can create an array of each character like this:
const characters = await Array.fromAsync('Marcus')
// ['M', 'a', 'r', 'c', 'u', 's']
Another example is the Node.js file system glob function used with promises. The glob
function returns an async iterator with all files matching the glob pattern. Depending on your code, you may want to show a message in case there aren’t any matches.
You can’t directly check the async iterator if it’s empty. You must consume the async iterator and detect the empty state manually. An option here is to convert the async iterable to an array and check if the array length is zero.
Here’s the sample code that converts the glob’s async iterable to a JavaScript array:
import { glob } from 'node:fs/promises'
const dbMigrationFiles = await glob('./database-migrations/**/*.js')
const migrations = await Array.fromAsync(dbMigrationFiles)
Array.isArray(migrations)
// true
if (migrations.length === 0) {
// handle "no matches"
}
Using a for await … of
Loop
You’re typically using a for await … of
 loop to consume an async iterable. If you need to support older browsers and don’t want to add a polyfill, you can use the for await … of
loop to create an array.
Here’s a short example using a for await … of
loop that iterates through each item and pushes it into a temporary array. You can then use the temporary array to check whether it’s empty or run your processing pipeline (using the typical array methods, like map
, filter
, and so on):
import { glob } from 'node:fs/promises'
const dbMigrationFiles = await glob('./database-migrations/**/*.js')
const migrations = []
for await (const file of dbMigrationFiles) {
migrations.push(file)
}
if (migrations.length === 0) {
// handle "no matches"
}
You can also build a reusable utility function that creates an array from an iterable input. The for await … of
loop supports iterables, async iterables, and array-like objects:
export function createArrayFromAsync(input) {
const items = []
for await (const item of input) {
items.push(file)
}
return items
}
That’s it!