繁体   English   中英

更新 async forEach 以根据来自另一个集合的属性更新每个文档属性

[英]Updating async forEach to update every document property based off property from another collection

我有这段代码有效。 但是,我不确定为什么,我觉得它的行为可能不一致。

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

我几乎找到了所有需要更新的列表,然后对于每个列表,我根据我之前检索到的数据更新其中一个属性。

到目前为止,它的行为一直正常,但我记得有人告诉我要避免在 forEach 循环中使用 async await,因为它的行为不像我们预期的那样。 但我不知道为什么这是有效的,我是否应该避免使用 forEach 并使用 forOf。 我也担心嵌套的异步等待。

有谁知道这样的事情是否可以? 有关我的应用程序的更多上下文

因为 forEach 循环中的回调是异步的,所以调用 forEach 之后的事情可能会在 forEach 完成之前执行,并且它不会等待每次迭代完成后再继续。
例如,在updateOne调用之前的 await 实际上是没有意义的,因为外部异步 function 没有被等待,这表明它可能没有按照您的意愿行事。

建议您不要在forEach中使用async的原因是它几乎永远不会按照您想要的方式运行,或者您实际上并不需要它,或者您可能会忘记您后来以这种方式调用它并且无意中导致稍后出现竞争条件(即:它使执行顺序难以推理并且几乎不可预测)。 如果您实际上不关心调用的结果、调用的时间和解析的时间,那么它是有效的。

下面是一个演示,展示了它如何导致意外结果。 随机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>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM