Обновление asyn c forEach для обновления каждого свойства документа на основе свойства из другой коллекции - PullRequest
0 голосов
/ 12 июля 2020

У меня есть код, который работает. Однако я не уверен, почему, и мне кажется, что это может вести себя непоследовательно. свойства, основанные на данных, которые я получил ранее.

До сих пор он вел себя надлежащим образом, но я помню, как мне говорили избегать использования asyn c await в forEach l oop, потому что он ведет себя не так, как мы ожидали. Но я не могу понять, почему это работает, и следует ли мне избегать forEach и использовать forOf. Меня также беспокоит наличие вложенных asyn c awaits.

Кто-нибудь знает, нормально ли что-то вроде этого? Для получения дополнительной информации о моем приложении

1 Ответ

0 голосов
/ 12 июля 2020

Поскольку обратный вызов в forEach l oop является asyn c, вещи, которые следуют за вызовом forEach, могут выполняться до завершения forEach, и он не будет ждать завершения каждой итерации sh перед продолжением. Например, ожидание перед вызовом updateOne на самом деле бессмысленно, поскольку внешняя функция asyn c не ожидается, что показывает, что она, вероятно, ведет себя не так, как вы предполагали.

Причина, по которой вам не рекомендуется использовать async внутри forEach, заключается в том, что он почти никогда не ведет себя так, как вы намереваетесь, или вам это на самом деле не нужно, или вы можете забыть, что вы назвали его так позже и непреднамеренно вызвать состояние гонки позже (ie: это делает порядок выполнения трудным для понимания и практически непредсказуемым). Его можно использовать, если вы действительно не заботитесь о результатах вызова, когда они вызываются и когда они разрешаются.

Ниже приведена демонстрация, показывающая, как это может привести к неожиданным результатам. sleep со случайным временем, чтобы каждый вызов .updateOne разрешался в случайное время. Обратите внимание, что вызов console.log(`Will execute before...`) выполняется перед итерациями forEach.

async function runit() {
  await Listing.find({}, (err, listings) => {
    if (err) {
      console.log(err);
    }
    listings.forEach(async (listing) => {
      //console.log(listing);
      let championsUpdate = {};
      for (let key in listing["champions"]) {
        championsUpdate[key] = rankingDB[key];
      }

      await Listing.updateOne(
        { _id: listing._id },
        { $set: { champions: championsUpdate } }
      );
    });
  });
  
  console.log(`Will execute before the updateOne calls in forEach resolve`)
}
runit()
<script>
// mock data with sequential _id from 0...9
listings = Array(10).fill().map((_,_id)=>({_id, champions: {1:1,2:2}}))
rankingDB = Array(10).fill({1:1.1,2:2.2})

// Promise that resolves in ms milliseconds
function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

// mock Listing
Listing = {
  find(x, fn) {
    console.log('find')
    return new Promise(res=>
      res(fn(undefined,listings)))
  },
  // updateOne resolves after random time < 1000 ms
  async updateOne({_id}) {
    await sleep(Math.random()*1000)
    console.log('updateOne with listing _id=',_id)
  }
}
</script>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...