When using async await in loops, you might encounter some of these problems.
If you just try to use await inside forEach
, this will throw an Unexpected token
error.
(async() => {
data = [1, 2, 3, 4, 5];
data.forEach(e => {
const i = await somePromiseFn(e);
console.log(i);
});
})();
This comes from the fact that you've erroneously seen the arrow function as a block. The await
will be in the context of the callback function, which is not async
.
The interpreter protects us from making the above error, but if you add async
to the forEach
callback no errors get thrown. You might think this solves the problem, but it won't work as expected.
Example:
(async() => {
data = [1, 2, 3, 4, 5];
data.forEach(async(e) => {
const i = await somePromiseFn(e);
console.log(i);
});
console.log('this will print first');
})();
This happens because the callback async function can only pause itself, not the parent async function.
You could write an asyncForEach function that returns a promise and then you could something like
await asyncForEach(async (e) => await somePromiseFn(e), data )
Basically you return a promise that resolves when all the callbacks are awaited and done. But there are better ways of doing this, and that is to just use a loop.
You can use a for-of
loop or a for/while
loop, it doesn't really matter which one you pick.
(async() => {
data = [1, 2, 3, 4, 5];
for (let e of data) {
const i = await somePromiseFn(e);
console.log(i);
}
console.log('this will print last');
})();
But there's another catch. This solution will wait for each call to somePromiseFn
to complete before iterating over the next one.
This is great if you actually want your somePromiseFn
invocations to be executed in order but if you want them to run concurrently, you will need to await
on Promise.all
.
(async() => {
data = [1, 2, 3, 4, 5];
const p = await Promise.all(data.map(async(e) => await somePromiseFn(e)));
console.log(...p);
})();
Promise.all
receives an array of promises as its only parameter and returns a promise. When all of the promises in the array are resolved, the returned promise is also resolved. We await
on that promise and when it's resolved all our values are available.
The above examples are fully runnable. The somePromiseFn
function can be made as an async echo function with a timeout.
You can try out the examples in the babel-repl with at least the stage-3
preset and look at the output.
function somePromiseFn(n) {
return new Promise((res, rej) => {
setTimeout(() => res(n), 250);
});
}