[英]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.