Поскольку обратный вызов в 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>