When I use async/await with rawCollection and get data inside a loop, and then set new properties (poTmp), it works as expected. But after I set new properties and console.log outside it doesn't set new props. Why?
let items = await Items.rawCollection()
.aggregate([
{
$match: match,
},
])
.toArray()
let data = items
data.forEach(async item => {
let po = await PurchaseOrderDetails.rawCollection()
.aggregate([
{
$match: {
itemId: item._id,
tranDate: { $lte: tDate },
},
},
{
$group: {
_id: '$itemId',
itemDoc: { $last: item },
onHandPO: { $sum: '$qtyBase' },
},
},
{
$group: {
_id: '$itemId',
itemDoc: { $last: '$itemDoc' },
lastOnHandPO: { $last: '$onHandPO' },
},
},
])
.toArray()
//==================
//set new properties
//==================
item.poTmp = po[0]
})
console.log(data)
return data
When I use async/await with rawCollection and get data inside a loop, and then set new properties (poTmp), it works as expected. But after I set new properties and console.log outside it doesn't set new props. Why?
let items = await Items.rawCollection()
.aggregate([
{
$match: match,
},
])
.toArray()
let data = items
data.forEach(async item => {
let po = await PurchaseOrderDetails.rawCollection()
.aggregate([
{
$match: {
itemId: item._id,
tranDate: { $lte: tDate },
},
},
{
$group: {
_id: '$itemId',
itemDoc: { $last: item },
onHandPO: { $sum: '$qtyBase' },
},
},
{
$group: {
_id: '$itemId',
itemDoc: { $last: '$itemDoc' },
lastOnHandPO: { $last: '$onHandPO' },
},
},
])
.toArray()
//==================
//set new properties
//==================
item.poTmp = po[0]
})
console.log(data)
return data
Share
Improve this question
edited Jun 4, 2020 at 10:54
insipidlight
172 silver badges5 bronze badges
asked Jul 3, 2018 at 14:50
phan chanraphan chanra
691 silver badge9 bronze badges
2 Answers
Reset to default 7The issue is here:
data.forEach(async item => {
async functions return promises immediately when invoked, so the forEach pletes and moves on to the console.log
line.
The async work delegated to the async functions has not yet pleted at this point and so the data is not yet modified.
Since it seems you are already in an async function (since you've used await higher in the code), you can wait for all the promises to resolve with await
and Promise.all
Promise.all
expects an array of promises, so instead of forEach
we can use map
to create an array of promises
await Promise.all( data.map(async item => { ...
Like forEach
, map
will iterate over all of the items and run the given function. Unlike forEach
, map
will return an array with the results of these functions, and given they are async functions, each one will return a promise.
Now we use Promise.all
to create a single promise that will resolve when every one of the async functions have finished. And we use await
to tell the function to pause until that new promise resolves.
Pausing the function with await
means the console.log won't run until every one of those async functions have finished, meaning it will have the correct data when it does run.
Here is a working example anyone who needs it:
public async updateBatch(req:any,res:any){
body = [1,3,4,5,6]
var rfinal:number[] = [];
await Promise.all(body.map(async items=>{
let newStatus = 'MJ';
inputParameters = [
{ name: 'PID', dataType: sql.Int, value: items },
{ name: 'Status', dataType: sql.VarChar, value: newStatus }
];
let CustomQuery = `UPDATE MYTable
SET Status= @Status
WHERE PID= @PID`;
const result = await provider.executeQuery(CustomQuery, inputParameters).catch(err => {
LogErrors.logErrors(err);
});
if(result.rowsAffected[0]>0){
rfinal.push(result.rowsAffected[0]);
}else{
throw new console.error('Un expected error, updating newsfeed');
}
})
);
}