for-await-of
and synchronous iterablesThis blog post describes how for-await-of
handles synchronous iterables. for-await-of
is a core construct of asynchronous iteration. You can read up on it in the blog post “ES proposal: asynchronous iteration”.
Note: you can run the examples in Node.js 9.2+ via:
node --harmony-async-iteration
Asynchronous iterables return asynchronous iterators, whose method next()
returns Promises for {value, done}
objects:
// Asynchronous generator
async function* asyncGen() {
yield 'a';
yield 'b';
}
const iter = asyncGen()[Symbol.asyncIterator]();
iter.next().then(x => console.log(x));
// { value: 'a', done: false }
iter.next().then(x => console.log(x));
// { value: 'b', done: false }
iter.next().then(x => console.log(x));
// { value: undefined, done: true }
for-await-of
Synchronous iterables return synchronous iterators, whose method next()
returns {value, done}
objects. for-await-of
handles synchronous iterables by converting them to asynchronous iterables. Each iterated value is converted to a Promise (or left unchanged if it already is a Promise) via Promise.resolve()
. That is, for-await-of
works for iterables over Promises and over normal values. The conversion looks like this:
const nextResult = Promise.resolve(valueOrPromise)
.then(x => ({ value: x, done: false }));
Two more ways of looking at the conversion are:
Iterable<Promise<T>>
becomes AsyncIterable<T>
The following object
{ value: Promise.resolve(123), done: false }
is converted to
Promise.resolve({ value: 123, done: false })
Therefore, the following two statements are roughly similar.
for (const x of await Promise.all(syncIterableOverPromises));
for await (const x of syncIterableOverPromises);
The second statement is faster, because Promise.all()
only creates the Promise for the Array after all Promises in syncIterableOverPromises
are fulfilled. And for-of
has to await that Promise. In contrast, for-await-of
starts processing as soon as the first Promise is fulfilled.
for-await-of
in action Iterating over a sync iterable over Promises:
async function main() {
const syncIterable = [
Promise.resolve('a'),
Promise.resolve('b'),
];
for await (const x of syncIterable) {
console.log(x);
}
}
main();
// Output:
// a
// b
Iterating over a sync iterable over normal values:
async function main() {
for await (const x of ['c', 'd']) {
console.log(x);
}
}
main();
// Output:
// c
// d