Reputation: 15
I'm using Node.js and mongoose to fetch data. Before sending the result i want to loop through the data and fetch some additional informations of a another mongoose collection.
I don't get it to manage, that the first mongoose call waits, that the other promises are finished. I assume that my mixture of promises and .then calls is a mess... :S
My Code:
exports.checkChats = (req, res, next) => {
console.log(req.userData.userId);
let modifiedChats = [];
let partner;
Messages.find({ $or: [{ "users.starter": req.userData.userId }, { "users.partner": req.userData.userId }] })
.then(chats => {
if (chats) {
console.log('Start');
const promises = chats.map(async chat => {
if (chat.users.starter.toString() === req.userData.userId.toString()) {
console.log('fetch partnerData for partner ' + chat.users.partner);
partner = chat.users.partner;
}
if (chat.users.partner.toString() === req.userData.userId.toString()) {
console.log('fetch partnerData for starter ' + chat.users.starter);
partner = chat.users.starter;
}
User.findById(partner).then(fetchedPartner => {
console.log('Partner fetched: ', fetchedPartner.userName);
modifiedChats.push({ id: chat._id, status: chat.status, lastMessage: chat.lastMessage, partnerName: fetchedPartner.userName, partnerImg: fetchedPartner.imagePath });
});
});
Promise.all(promises).then(() => {
console.log('End');
res.status(201).json({
message: 'Chats found',
chats: modifiedChats
});
});
}
}).catch(error => {
res.status(500).json({
message: 'Couldnt fetch chats!',
error: error
});
});
};
And the Terminallog:
Start
fetch partnerData for partner 5eb8502aad51b72012a2ccd1
fetch partnerData for partner 5eb84c93ad51b72012a2cc56
End
Partner fetched: Summer
Partner fetched: Pepe
Thank you guys.
Upvotes: 0
Views: 35
Reputation: 1244
Hi so the problem in your code is located here
const promises = chats.map(async chat => {
if (chat.users.starter.toString() === req.userData.userId.toString()) {
console.log('fetch partnerData for partner ' + chat.users.partner);
partner = chat.users.partner;
}
if (chat.users.partner.toString() === req.userData.userId.toString()) {
console.log('fetch partnerData for starter ' + chat.users.starter);
partner = chat.users.starter;
}
User.findById(partner).then(fetchedPartner => {
console.log('Partner fetched: ', fetchedPartner.userName);
modifiedChats.push({ id: chat._id, status: chat.status, lastMessage: chat.lastMessage, partnerName: fetchedPartner.userName, partnerImg: fetchedPartner.imagePath });
});
});
Every iteration of this loop calls User.findById(partner)
which returns a promise and is asynchronous. The function does not know that it needs to wait for its result.
One way to tell the function to wait for User.findById(partner)
to finish is by adding return
.
return User.findById(partner).then(fetchedPartner => {
console.log('Partner fetched: ', fetchedPartner.userName);
modifiedChats.push({ id: chat._id, status: chat.status, lastMessage: chat.lastMessage, partnerName: fetchedPartner.userName, partnerImg: fetchedPartner.imagePath });
});
You can also remove the async in
const promises = chats.map(async chat => {
It serves no purpose, because you are not utilizing async/await
anyway.
Here's also an example to refactor your code using async/await
.
exports.checkChats = async (req, res, next) => { // by putting async here, you are enabling a function to use await
try {
console.log(req.userData.userId);
let partner;
const chats = await Messages.find({ $or: [{ "users.starter": req.userData.userId }, { "users.partner": req.userData.userId }] }); // by putting await before a function call the returns a promise, you will have the access to the value it will return by this syntax
console.log('Start');
const promises = chats.map(async chat => { // by putting async here, you are enabling a function to use await; This is also a function so you will need to put async here if you want to use await inside
if (chat.users.starter.toString() === req.userData.userId.toString()) {
console.log('fetch partnerData for partner ' + chat.users.partner);
partner = chat.users.partner;
}
if (chat.users.partner.toString() === req.userData.userId.toString()) {
console.log('fetch partnerData for starter ' + chat.users.starter);
partner = chat.users.starter;
}
const fetchedPartner = await User.findById(partner); // same here, I put await because its a promise
console.log('Partner fetched: ', fetchedPartner.userName);
return { id: chat._id, status: chat.status, lastMessage: chat.lastMessage, partnerName: fetchedPartner.userName, partnerImg: fetchedPartner.imagePath }; // I am just returning the values here. By the end of the .map() it will have created an array of promises that each resolves the values return here
});
const modifiedChats = await Promise.all(promises); // this will convert the array of promises to an array of the values that was resolve by each of the promises.
console.log('End');
res.status(201).json({
message: 'Chats found',
chats: modifiedChats
});
} catch (error) { // using try catch is the way for catching thrown errors / rejected promises
res.status(500).json({
message: 'Couldnt fetch chats!',
error: error
});
}
};
Upvotes: 1