I have an async await function that uses mongoose:
const createModelB = async (id) => {
try {
let user = await User.findOne({id: id});
if (user) {
let modelB = new ModelB({ user_id: user.id });
modelB = await scrum.save();
return modelB;
}
return null;
} catch (err) {
console.error(err);
}
return null;
};
Now then I'm calling this function from somewhere else:
let modelB = createModelB(123);
console.log(modelB);
Instead of outputting Models fields, console returns to me this:
Promise {<pending>}
What did I miss?
I think it will be
const createModelBInstance = async () => {
let modelB = await createModelB(123);
console.log(modelB);
}
createModelBInstance();
as long as async functions are returning values you have to call an await on them, and should be wrapped inside another async function.
Related
I am creating a query to my postgres database. The function that makes the query looks like this:
const { pool } = require("./database");
async function getClasses(user) {
return pool.connect(async function (err, client, done) {
if (err) {
console.log(err);
} else {
const sqlText = `SELECT * FROM "public"."classes" WHERE "admins" = $1`;
const values = [user];
let listOfClasses = await client.query(sqlText, values);
done();
console.log(listOfClasses.rows);
return listOfClasses.rows;
}
});
}
module.exports = { getClasses };
The console.log(listOfClasses.rows) has the rows that I am looking for my function to return, but the actual returned value is undefined. I've been tinkering with the code for quite a while now and can't seem to figure it out. Any help here would be much appreciated.
You have to use the promise style api as the callback style call will not return anything (Your function is retuning client.connect(() => { ... }) which return undefined)
const getClasses = (...) => {
const client = await pool.connect()
const listOfClasses = await client.query(...);
return listOfClasses.rows;
}
should do
I have this route to delete a "garage" from the mongodb database and then grab some of the remaining garages. For some reason it is still returning the deleted garage and returning it, but if I check the database the delete was successful.
router.post('/garage/delete', requireLogin, async (req, res) => {
let limit = 20;
try {
let list = req.body;
list.map( async (item) => {
const existingGarage = await Garage.find({_id: item._id});
if (existingGarage) {
await Garage.deleteOne({_id: item._id});
} else {
res.status(400).send("Garage not found");
}
})
const allGarages = await Garage.find().limit( limit );
console.log(allGarages);
res.send(allGarages);
} catch {
res.status(400).send("Garage not found");
}
})
You will need to await all the promises returned by the map function.
Promise.all awaits an array of promises and runs them in parallel.
since you're passing an async function to the map function you will need to await all the promises returned by that async function
Other solution is to use a for of loop
router.post('/garage/delete', requireLogin, async (req, res) => {
let limit = 20;
try {
let list = req.body;
await Promise.all(list.map( async (item) => {
const existingGarage = await Garage.find({_id: item._id});
if (existingGarage) {
await Garage.deleteOne({_id: item._id});
} else {
res.status(400).send("Garage not found");
}
}))
const allGarages = await Garage.find().limit( limit );
console.log(allGarages);
res.send(allGarages);
} catch {
res.status(400).send("Garage not found");
}
})
const returnPostData = async (req, res, initialPostsQueryArray) => {
const promises = initialPostsQueryArray.map((post) => {
let postObject;
voteCount = sumValues(post.voting_options);
pool.query('SELECT voting_option FROM votes WHERE user_id = $1 AND post_id = $2',
[req.user.userId, post.post_id],
(error, results) => {
if (error) {
console.log(error);
return res.json({'Error': error.detail});
}
userVoteOption = results.rows[0].voting_option;
});
postObject.voteCount = voteCount;
postObject.userVoteOption = userVoteOption;
return postObject;
});
return Promise.all(promises).then(postData => {
return res.json(postData);
})
}
I'm trying to return an array of the postObjects for each post. For some reason it keeps on printing null for these objects because the return res.json is somehow running before the promises are even done. Any help is appreciated.
I had this problem before and used the same code, but it didn't work for this one for some reason.
Multiple problems:
You return postObject before userVoteOption is assigned
You don't actually use promises with node-postgres
…and therefore the return value of the map callback is not a promise
You never initialse postObject
You marked your function as async but never use await
You handle each error in the loop individually (which leads to res.json being called multiple times in case of multiple errors)
To fix these, use
async function returnPostData(req, res, initialPostsQueryArray) {
try {
const promises = initialPostsQueryArray.map(async (post) => {
const voteCount = sumValues(post.voting_options);
const results = await pool.query(
'SELECT voting_option FROM votes WHERE user_id = $1 AND post_id = $2',
[req.user.userId, post.post_id]
);
const userVoteOption = results.rows[0].voting_option;
const postObject = { voteCount, userVoteOption };
return postObject;
});
const postData = await Promise.all(promises);
res.json(postData);
} catch(error) {
console.log(error);
res.json({'Error': error.detail});
}
}
In addition, you actually shouldn't use a loop at all here. Just query multiple rows from postgres at once! Using this approach to supply the ids:
async function returnPostData(req, res, initialPostsQueryArray) {
try {
const voteCounts = new Map();
const ids = [];
for (const post of initialPostsQueryArray) {
ids.push(post.post_id);
voteCounts.set(post.post_id, sumValues(post.voting_options));
}
const {rows} = await pool.query(
'SELECT post_id, voting_option FROM votes WHERE user_id = $1 AND post_id = ANY($2::int[])',
[req.user.userId, ids]
);
const postData = rows.map(row => {
const postObject = {
voteCount: voteCounts.get(row.post_id),
userVoteOption: row.voting_option,
};
return postObject;
});
const postData = await Promise.all(promises);
res.json(postData);
} catch(error) {
console.log(error);
res.json({'Error': error.detail});
}
}
I am implementing a Cloud Function where I am executing several queries.
let rating_subcollection = await admin.firestore().collection("restaurants_collection").doc(vendor_id).collection('ratings').where('uID', '==', userId)
.get()
.then(async function (data) {
if (data.empty) {
let restaurants_collection = admin.firestore().collection("restaurants_collection").doc(vendor_id);
await admin.firestore().runTransaction(async (transaction) => {
const restDoc = await transaction.get(restaurants_collection);
// Compute new number of ratings
const newNumRatings = restDoc.data().noRat + 1;
// Compute new average rating
const oldRatingTotal = restDoc.data().rat * restDoc.data().noRat;
const newAvgRating = (oldRatingTotal + ratingVal) / newNumRatings;
// Update restaurant info
transaction.update(restaurants_collection, {
rat: newAvgRating,
noRat: newNumRatings
});
})
}
}).catch(error => {
return "Couldnt update the rating: " + error;
})
So, as you can see I am only executing the transaction IF data is empty and I have set async in the then() callback. Is this the right way to do?!
I found this example, where is explained on detail how get a collection using an async function, for example if you want to get a collection:
function getValues(collectionName, docName) {
return db.collection(collectionName).doc(docName).get().then(function (doc) {
if (doc.exists) return doc.data().text;
return Promise.reject("No such document");
}};
}
Or using await it inside an async function in a try/catch block, if you like that better:
async function doSomething() {
try {
let text = await getValues('configuration','helpMessage');
console.log(text);
} catch {
console.log("ERROR:" err);
}
}
I am working on a nodejs code that fetches data from a site, parses it, finds particular data and fetches something else for the data that was previously fetched. But the final return statement is returning without the value fetched from the second API call.
I tried to implement async await, but I am not sure where do I have to put them exactly.
const getMainData = async val => {
let result = [];
//get xml data from the API
const xmlData = await getSiteContent(`value`); //axios call
parseString(xmlData, (err, json) => { //convert xml to json
const { entry } = json.feed; // array of results.
result = entry.map(report => {
const secondInfo = getSomeMoreData(report.something); //axios call
const data = {
id: report.id,
date: report.date,
title: report.title
};
data.info = secondInfo;
return data;
});
});
return { result };
};
I was expecting the function to return the array result that has id, date, title and info. But I am getting info as null since it is going to another function that does one more API call.
Try wrapping parseString in a promise so you can await the result, then make the entry.map callback an async function so that you can use the await keyword to wait for the result of the axios fetch.
async function xml2json(xml) {
return new Promise((resolve, reject) => {
parseString(xml, function (err, json) {
if (err)
reject(err);
else
resolve(json);
});
});
}
const getMainData = async val => {
//get xml data from the API
const xmlData = await getSiteContent(`value`); //axios call
const json = await xml2json(xmlData);
const { entry } = json.feed; // array of results
const result = await Promise.all(
entry.map(async report => {
const secondInfo = await getSomeMoreData(report.something); // axios call
const data = {
id: report.id,
date: report.date,
title: report.title,
};
data.info = secondInfo;
return data;
})
)
return { result };
}
Let me know if that works. If not, I can try to help you out further.
The problem with your code is you have mixed promises concept(async/await is a syntactic sugar - so same thing) along with callback concept.
And the return statement is outside callback() of parseString() and the callback would be executed maybe after returning results only because parseString() is an asynchronous function.
So in the following solution I have wrapped parseString() in a promise so that it can be awaited.
const parseStringPromisified = async xmlData => {
return new Promise((resolve, reject) => {
parseString(xmlData, (err, json) => {
if (err) {
reject(err);
}
resolve(json);
});
});
};
const getMainData = async val => {
//get xml data from the API
const xmlData = await getSiteContent(`value`); //axios call
const json = await parseStringPromisified(xmlData);
const { entry } = json.feed; // array of results.
const result = entry.map(async report => {
const secondInfo = await getSomeMoreData(report.something); //axios call
return {
id: report.id,
date: report.date,
title: report.title,
info: secondInfo
};
});
return Promises.all(result);
};