Why does this snippet
const firstArray = ['toto', 'toto'];
const secondArray = ['titi', 'titi'];
firstArray.forEach(async (toto, i) =>
{
await secondArray.forEach(async titi =>
{
// async code
console.log(titi, i);
});
// async code
console.log(toto, i);
});
produce the following output:
Removing the await
keyword produces the expected output
My guess is it resides in the await
keyword's behaviour, as, without which, the produced output is the expected output.
EDIT: this is a purely trivial question. I want to understand why using await before a forEach provides this behaviour. There is no 'concrete code' behind this.
EDIT2: edited the snippet as the ments and answer reflected misunderstanding of my question
Why does this snippet
const firstArray = ['toto', 'toto'];
const secondArray = ['titi', 'titi'];
firstArray.forEach(async (toto, i) =>
{
await secondArray.forEach(async titi =>
{
// async code
console.log(titi, i);
});
// async code
console.log(toto, i);
});
produce the following output:
Removing the await
keyword produces the expected output
My guess is it resides in the await
keyword's behaviour, as, without which, the produced output is the expected output.
EDIT: this is a purely trivial question. I want to understand why using await before a forEach provides this behaviour. There is no 'concrete code' behind this.
EDIT2: edited the snippet as the ments and answer reflected misunderstanding of my question
Share Improve this question edited Apr 17, 2018 at 10:10 mjarraya asked Apr 17, 2018 at 9:24 mjarrayamjarraya 1,2261 gold badge14 silver badges19 bronze badges 9-
I get
titi titi titi titi toto toto
from your snippet and the other version if I move the first await to the secondconsole.log
, I may be missing something though? – Josh Rumbut Commented Apr 17, 2018 at 9:31 -
2
But
await
-ing the returned value offorEach
does not make sense anyway, asawait
is made to receive aPromise
, so that your code looks synchronous while being asynchronous, butforEach
returnsundefined
. This does not explain why your snippet behaves the way you say though. – sp00m Commented Apr 17, 2018 at 9:33 - 1 You should post all your code – Oscar Paz Commented Apr 17, 2018 at 9:35
- @JoshRumbut We get the same output. Might edit the question to make it clearer. The first await isn't to be moved, it is to be removed – mjarraya Commented Apr 17, 2018 at 9:35
- 2 When using async/await it's better to stay in the same scope so a foo...of loop is better then forEach – Endless Commented Apr 17, 2018 at 9:40
4 Answers
Reset to default 5forEach works synchronously meaning it doesn't do anything with the return of each iteration (in this case, the promise) and it ignores it.
If you use instead:
for(let item of arrayItems)
or a normal for loop you should see the expected result working asynchronously.
I guess you want to await all promises before continuing. If should be the case, a bination of Array.prototype.reduce(...)
, Array.prototype.map(...)
and Promise.all(...)
will better suit:
const allPromises = firstArray.reduce((accumulatedPromises, toto) => {
const secondArrayPromises = secondArray.map((titi) => {
return // some promise;
});
const firstArrayPromise = // some promise;
return accumulatedPromises.concat(secondArrayPromises, firstArrayPromise);
}, []);
const finalPromise = Promise.all(allPromises);
Then, either await finalPromise
if you're within an async function
, or use .then(...)
.
Your outer forEach
loop spits out two functions (note the async
type you have; async functions automatically return a Promise) directly on the stack that then runs in sequence before finally outputting from the firstArray
. So after both the functions are executed your output for the outer loop is printed. This is how the forEach
loop works with async
functions.
To use async/await
with loops, you would want to have:
async function boot() {
for(let item of list) {
await getData(); // Here getData returns a promise
}
}
boot();
Another way could be to use bluebird for Promise
:
let exec = [];
await Promise.map(list => getData());
// Here Promise.map iterates over list
// and returns a list of promises which are
// then resolved using await
The key to understanding is that await
is asynchronous. So everything after will be executed no earlier than the current execution thread returns. Thus, the part
// async code
console.log(toto);
is put to a queue rather than being executed immediately.
Async/await essentially wraps your code like this:
const firstArray = ['toto', 'toto'];
const secondArray = ['titi', 'titi'];
firstArray.forEach((toto,i) => new Promise((resolve)=>
{
resolve((new Promise((resolve)=>resolve(secondArray.forEach(titi => new Promise((resolve)=>
{
resolve(console.log(titi, i));
}))))).then(()=>console.log(toto,i))
);}));
Now consider this code as well (setTimeout
is pretty much a promise that is resolved after a given amount of time):
const firstArray = ['toto', 'toto'];
const secondArray = ['titi', 'titi'];
firstArray.forEach((toto, i) =>
{
secondArray.forEach(titi =>
{
console.log(titi, i);
});
setTimeout(()=>console.log(toto, i));
});