I know there are plenty of resources explaining for await...of
, but I don’t think I’ll ever fully grasp the concept until I see an example that works the exact same way but with more basic syntax.
Thing is, if my understanding is correct, these 2 for
loops should behave the exact same way:
for(const p of iterableWithPromises) {
const q = await p;
// ...
}
for await (const q of iterableWithPromises) {
// ...
}
Is that it? Is this syntax only saving us from creating a new variable inside the loop to store the promise’s result?
2
Answers
No, the 2 loops are not exactly equivalent.
First things first: This is, roughly speaking, what the equivalent of the traditional
for ...of
loop looks like with normal iterables (omiting corner cases like the usage of break, continue, exceptions and returns in the loop for brevity):Now, then, with
for await...of
and async iterables, the equivalent would be like this:Now let's say my initial wrong assumption about how this kind of for loop works was right. Then we should be able to replace the async iterable with a normal iterable, like so:
If you run any example with this last iterable you will notice no difference in the results. Not even in the times between one output and the next. But there is something you should notice: The
done
property of the object returned bynext()
is included inside the promise when usingfor await...of
. This is relevant in cases where deciding whether the for loop should stop iterating depends on the result of the promise.For instance, let's say you have a REST api that has in one of the fields of the response json object a url to keep fetching the next results: You could still technically implement this with a normal iterator, but with a few caveats:
next()
is evaluated you are forced to make suredone
is evaluated totrue
regardless of whether the REST api has actually any data or not to make sure at least the first request is made, otherwise no request would be made to begin with and you wouldn't have any way to tell if there is any data at all (if it evaluated to false, the request would still be done but the loop would end without any chance to do anything with the data and you won't be able to do anything about it).done
property for the first time. You can prevent this by fetching the data in the server implementing async iterators as I did in the examples (Notice how in thefor await...of
example I use[Symbol.asyncIterator]
instead of[Symbol.iterator]
) to force the developer to usefor await...of
and prevent these problems.Of course. You are correct that the two loops share a similar purpose: they both iterate over an iterable containing promises and allow you to handle their resolved values. However,
for await...of
provides cleaner and more concise syntax specifically designed to handle asynchronous iterables, while your first example is a more verbose way of achieving the same result with a regularfor
loop.Why use
for await...of
?The for
await...of
loop is specifically useful when dealing with asynchronous iterables, which provide values over time, such as streams or generators that yield promises.Example: Using an asynchronous generator
This cannot be easily replicated with a
for...of
loop becausefor...of
does not inherently understand asynchronous iterables.And if you want to get the hood , go for the sepc.