最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Await nested forEach behaviour - Stack Overflow

programmeradmin2浏览0评论

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 second console.log, I may be missing something though? – Josh Rumbut Commented Apr 17, 2018 at 9:31
  • 2 But await-ing the returned value of forEach does not make sense anyway, as await is made to receive a Promise, so that your code looks synchronous while being asynchronous, but forEach returns undefined. 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
 |  Show 4 more ments

4 Answers 4

Reset to default 5

forEach 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));
});

发布评论

评论列表(0)

  1. 暂无评论