简体   繁体   English

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

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

I have this piece of code that works.我有这段代码有效。 However, I am not sure why and I feel like it might behave inconsistently.但是,我不确定为什么,我觉得它的行为可能不一致。

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

Pretty much I am finding all the listings that I need to update, and then for each listing I am updating one of the properties based off the data I retrieved earlier.我几乎找到了所有需要更新的列表,然后对于每个列表,我根据我之前检索到的数据更新其中一个属性。

So far it's been behaving appropriately but I remember being told to avoid using async await in a forEach loop because it does not behave as we expect.到目前为止,它的行为一直正常,但我记得有人告诉我要避免在 forEach 循环中使用 async await,因为它的行为不像我们预期的那样。 But I can't figure out why this is working and if I should avoid the forEach and use a forOf.但我不知道为什么这是有效的,我是否应该避免使用 forEach 并使用 forOf。 I am also worried about having nested async awaits.我也担心嵌套的异步等待。

Does anyone know if something like this is ok?有谁知道这样的事情是否可以? For more context on my application有关我的应用程序的更多上下文

Because the callback in the forEach loop is async, things that follow the call to forEach may execute before forEach finishes, and it will not wait for each iteration to finish before continuing.因为 forEach 循环中的回调是异步的,所以调用 forEach 之后的事情可能会在 forEach 完成之前执行,并且它不会等待每次迭代完成后再继续。
For example the await in front of the updateOne call is actually pointless, since the outer async function isn't awaited on, which shows that it is probably not behaving the way you intend it to.例如,在updateOne调用之前的 await 实际上是没有意义的,因为外部异步 function 没有被等待,这表明它可能没有按照您的意愿行事。

The reason it is recommended that you not use async inside a forEach is that it is almost never behaving the way you intend it to, or you don't actually need it, or you may forget that you called it that way later and unintentionally cause a race condition later (ie: it makes the execution order hard to reason about and virtually unpredictable).建议您不要在forEach中使用async的原因是它几乎永远不会按照您想要的方式运行,或者您实际上并不需要它,或者您可能会忘记您后来以这种方式调用它并且无意中导致稍后出现竞争条件(即:它使执行顺序难以推理并且几乎不可预测)。 It has valid use if you actually do not care about the results of the call, when they are called, and when they resolve.如果您实际上不关心调用的结果、调用的时间和解析的时间,那么它是有效的。

Below is a demo showing how it can cause unexpected results.下面是一个演示,展示了它如何导致意外结果。 sleep with random time to make each .updateOne call resolve at random times.随机sleep以使每个.updateOne调用在随机时间解决。 Notice that call to console.log(`Will execute before...`) executes before the forEach iterations.请注意,对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.

相关问题 根据另一个Meteor集合的属性过滤一个Meteor集合 - Filtering one Meteor collection based off the property of another Meteor collection 根据MongoDB中一个文档的另一文档的属性获取结果 - Get result based on property of another document from one document in MongoDB 集合中嵌入文档的集合更新属性 - collection update property of embedded document in array 根据同一文档中的时间戳属性更新 Firestore 文档上的属性 - Update Property on Firestore Document Based on a timestamp property in same document 如何使用Meteor通过ID从另一个集合中的文档返回文档属性? - How do I return document property from document in another collection by ID using Meteor? 从对象列表更新基于另一个列表下划线的属性 - From a list of objects update a property based on another list underscorejs 使用 mongoose 更新文档(更新文档属性的属性) - Update a document with mongoose (update property of document property) 从ViewModel的属性中剔除更新可观察的集合 - knockout update observable collection from property on viewmodel 根据另一个属性的值更新嵌套的 object 属性 - JS - Update nested object property based on the value of another property - JS 从不更新父项的子项更新@Input属性 - update @Input property from child not updating parent
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM