Reputation: 4542
I have the method:
const getTaskOrCategoryRecursive = async (type, id)=>{
const rslt = await type.findOne.call(type, {_id:id},{}, standardOptions, err=>{
if(err){
return({err: err});
}
});
const result = rslt.toObject();
const tasks = result.tasks;
const events = result.events;
result.children = {};
result.children.tasks = tasks.map(async taskId=>{
const taskIdString = taskId.toString();
const rtrn = await getTaskRecursive(taskIdString, err=>{
if(err){
return({err: err});
}
});
return rtrn;
});
result.children.events = events.map(async eventId=>{
const eventIdString = eventId.toString();
await Event.findOne({_id:eventIdString}, standardOptions,err=>{
if(err){
return({err: err});
}
});
});
return result;
}
It's called by two methods:
const getTaskRecursive = (taskId)=>{
return getTaskOrCategoryRecursive(Task, taskId)
}
and
const getCategoryRecursive=(categoryId)=>{
return getTaskOrCategoryRecursive(Category,categoryId);
};
which are called by the function
router.get('/:id', verifyToken, async (req,res)=>{
Branch.getCategoryRecursive(req.params.id)
.then(
(cat)=>{
res.status(200).send(cat);
},
(err)=>{
res.status(500).send(err);
}
)
});
When I try to run the program, first the getCategoryRecursive
method runs, then res.status(200).send(cat);
then the getTasksRecursive
method which gets the children of the object being sent in the response. getTasksRecursive
does what it is supposed to, but it's running after the response is sent so the object is empty.
How do I make my asynchronous method run before the response is sent?
UPDATE: Using Aritra Chakraborty's answer, I changed it to the following and it worked.
First I separated the .map
into a new function:
const getAllTasksRecursive = async(taskIds)=>{
const rtrn = await Promise.all(
taskIds.map(
async taskId=>{
return await getTaskRecursive(taskId.toString(), err=>{if(err){return {err:err}}});
}
)
)
return rtrn;
}
Then I called it in the previous function using:
result.children.tasks = await getAllTasksRecursive(tasks);
Now I am getting the data back in the response.
Upvotes: 1
Views: 45
Reputation: 12542
That's because internally a map
or foreach
can look something like this:
Array.prototype.forEach = function (callback) {
// this represents our array
for (let index = 0; index < this.length; index++) {
// We call the callback for each entry
callback(this[index], index, this)
}
}
It will call the callback alright, but it will not wait for the callback to complete before running the next one.
So, you need one async foreach
of some sort,
async function asyncForEach(array, callback) {
for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array)
}
}
Note, how the callback is being await
ed.
Your code will have to be something like this:
const start = async () => {
await asyncForEach([1, 2, 3], async (num) => {
await waitFor(50)
console.log(num)
})
console.log('Done')
}
start()
Refer: https://codeburst.io/javascript-async-await-with-foreach-b6ba62bbf404 This article helped me alot in the past.
Upvotes: 1