for await...of
loop also consumes sync iterables and generators. In that case it internally awaits emitted values before assign them to the loop control variable.
function* generator() {
yield 0;
yield 1;
yield Promise.resolve(2);
yield Promise.resolve(3);
yield 4;
}
(async () => {
for await (const num of generator()) {
console.log(num);
}
})();
for (const numOrPromise of generator()) {
console.log(numOrPromise);
}
Note: Be aware of yielding rejected promises from sync generator. In such case for await...of
throws when consuming rejected promise and DOESN'T CALL finally
blocks within that generator. This can be undesirable if you need to free some allocated resources with try/finally
.
function* generatorWithRejectedPromises() {
try {
yield 0;
yield 1;
yield Promise.resolve(2);
yield Promise.reject(3);
yield 4;
throw 5;
} finally {
console.log('called finally');
}
}
(async () => {
try {
for await (const num of generatorWithRejectedPromises()) {
console.log(num);
}
} catch (e) {
console.log('caught', e);
}
})();
try {
for (const numOrPromise of generatorWithRejectedPromises()) {
console.log(numOrPromise);
}
} catch (e) {
console.log('caught', e);
}
To make finally
blocks of a sync generator to be always called use appropriate form of the loop, for await...of
for the async generator and for...of
for the sync one and await yielded promises explicitly inside the loop.
(async () => {
try {
for (const numOrPromise of generatorWithRejectedPromises()) {
console.log(await numOrPromise);
}
} catch (e) {
console.log('caught', e);
}
})();