Wait for mongoose.find inside foreach loop inside callback - node.js

How do I wait till data is populated in finalresult before going further?
I tried async and await but did not work as expected and on top of that I am new to nodejs express
exports.getporderlist = (req, res) => {
page = req.params.page || "1";
skip = (page - 1) * 10;
if (req.profile.role.includes(1)) {
Order.find({ status: { $in: [0, 2] } })
.select("_id updatedAt items salesman")
.populate("customer", "customer_name phone")
.sort({ updatedAt: -1 })
.skip(skip)
.limit(10)
.exec((err, orders) => {
if (err) {
return res.status(400).json({
error: "No Orders found",
});
}
let finalresult = [];
orders.forEach((oelement) => {
total = 0;
salesman = oelement.salesman.split(" ")[0];
itemscount = oelement.items.length;
placeorder = true;
oelement.items.forEach((element) => {
total += element.price * element.quantity;
});
//Wait for the bellow data variable to finish populating finalresult
const data = Design.find()
.select("_id sm lm")
.where("_id")
.in(oelement.items)
.exec((err, orders) => {
finalresult.push(orders);
console.log(orders);
});
});
console.log(1);
res.json(finalresult);//getting empty finalresult array on call
});
} else {
res.json({ Message: "Go away" });
}
};

The exec() function do returns promise, so you can user async/await to call it.
Your code will look like this:
exports.getporderlist = async (req, res) => {
try {
if (req.profile.role.includes(1)) {
page = Number(req.params.page) || 1;
skip = (page - 1) * 10;
const orders = await Order.find({
status: {
$in: [0, 2]
}
})
.select("_id updatedAt items salesman")
.populate("customer", "customer_name phone")
.sort({
updatedAt: -1
})
.skip(skip)
.limit(10)
.exec();
let finalresult = [];
for (const oelement of orders) {
let total = 0;
salesman = oelement.salesman.split(" ")[0];
itemscount = oelement.items.length;
placeorder = true;
oelement.items.forEach((element) => {
total += element.price * element.quantity;
});
const data = await Design.find()
.select("_id sm lm")
.where("_id")
.in(oelement.items)
.exec();
finalresult.push(data);
}
res.json(finalresult);
} else {
res.json({
Message: "Go away"
});
}
} catch (err) {
return res.status(400).json({
error: "No Orders found",
});
}
}
you can always call async functions with async/await inside for ..of loops. You can read more here
P.s. Couldn't get a chance to run the code.. Let me know if you have any doubts :)

Related

Mongoose exec() does not await properly

For some reason, exec() is not awaiting in my code:
let team = <SOME TEAM NAME> //putting manually for testing
let results = []
TeamModel.find({ name: team })
.exec(async (err, docs) => {
if (err)
return res.send(Response("failure", "Error occured while retrieving fixtures"))
for(let i = 0; i < docs.length; i++){
let doesExist = await FixtureModel.exists({ leagueName: docs[i].leagueName })
if (doesExist) {
let query = FixtureModel.find({ leagueName: docs[i].leagueName, $or: [{ homeTeam: team }, { awayTeam: team }] })
await query.exec((err2, docs2) => {
if (err2)
return res.send(Response("failure", "Error occured while retrieving fixtures"))
docs2.forEach((doc2, index) => {results.push(doc2.toObject())})
console.log('during await') //Executes second
})
console.log('after await') //Executes first
}
else { //This section is not required
let result = await Communicator.GetFixturesFromLeague(docs[i].leagueId)
result.api.fixtures.forEach((fixture, index) => {
let newFixture = new FixtureModel({
fixtureId: fixture.fixture_id,
leagueId: fixture.league_id,
leagueName: fixture.league.name,
eventDate: fixture.event_date,
statusShort: fixture.statusShort,
homeTeam: fixture.homeTeam.team_name,
awayTeam: fixture.awayTeam.team_name
})
newFixture.save()
results.push(newFixture.toObject())
})
}
}
console.log(results)
res.send(Response("success", "Retrieved fixtures", results))
})
and the result looks something like this:
after await
[]
during await
and therefore an empty array of results is sent before values are added inside. I'm not sure what I'm missing here.
This is a workaround if anyones wondering. Sending the response inside the exec() callback when the forLoop completes bypassing the need for async/await.
if (doesExist) {
let query = FixtureModel.find({ league_name: docs[i].leagueName, $or: [{ homeTeam: { team_name: team} }, { awayTeam: { team_name: team} }] })
await query.exec((err2, docs2) => {
if (err2)
return res.send(Response("failure", "Error occured while retrieving fixtures"))
docs2.forEach((doc2, index) => {results.push(doc2.toObject())})
if(i + 1 == docs.length){
return res.send(Response("success", "Retrieved fixtures", results))
}
})
}

Return inside map function is not working

I'm trying to get product details like price, discount by the id i'm getting from my cart. This return function is returning null. but working perfectly in console.log.
async function store (req,res) {
const item = req.session.cart;
const cart = new Cart(item);
const results = cart.generateArray();
let result = [];
result = results.map(item => {
Products.findOne({'_id': item.id}, function (err, r) {
if(err){
console.log(err);
} else {
return ({
product_id: r._id,
price: r.price,
qty: item.qty,
total: r.price*item.qty
});
// this return is not working
}
});
});
let data = await result;
return res.send(data);
}
You should use Promise.all to solve an array of promises:
result = results.map(async item => {
try {
const r = await Products.findOne({'_id': item.id})
return ({
product_id: r._id,
price: r.price,
qty: item.qty,
total: r.price*item.qty
});
} catch (err) {
console.log(err);
}
});
let data = await Promise.all(result);

How to return 2 arrays after saving data to mongodb using node js

I need help with code below. I get an array of items from the client then the goal is to save them in mongodb and return the list classified as 'saved' and 'failed' items. sample of failed items are those that are duplicate on a unique attribute.
I know the code below will not work because of variable scope. how do i get around it? the code below returns an empty array for both savedItems and failedItems. Thanks!
router.post('/addItems', async (req, res, next) => {
let items = req.body;
let result = {
savedItems: [],
failedItems: []
};
function saveData() {
for (i = 0; i < items.length; i++) {
item = items[i];
Model.create({ ...item }, (err, data) => {
if (err) {
result.failedItems.push(item);
} else {
result.savedItems.push(item);
}
});
}
return result;
}
saveData().then(result => {
res.send({
results: result
});
});
});
router.post('/addItems', async (req, res, next) => {
// use try catch when use async
try {
let items = req.body;
let result = {
savedItems: [],
failedItems: []
};
for (let i = 0; i < items.length; i++) {
const item = items[i];
// use the returned promise instead of callback for Model.create
const data = await Model.create({ ...item });
result.savedItems.push(item);
// if also need to handle failed item in result use anathor try catch inside
/*try {
const data = await Model.create({ ...item });
result.savedItems.push(item);
} catch( err ) {
result.failedItems.push(item);
}*/
}
res.send({
results: result
});
} catch( err ) {
// To all the errors unexpected errors + thrown rejected promises
res.send({
error: err
});
}
});
Your saveData method didn't return a promise, try this
function saveData() {
return new Promise(resolve => {
let items = req.body;
let result = {
savedItems: [],
failedItems: []
};
let promises = [];
for (i = 0; i < items.length; i++) {
item = items[i];
let promise = new Promise(resolve => {
Model.create({ ...item }, (err, data) => {
if (err) {
result.failedItems.push(item);
} else {
result.savedItems.push(item);
}
resolve();
});
});
promises.push(promise);
}
Promise.all(promises).then(() => resolve(result));
})
}

Code executes before i get the response from the database

I am new to Express and writing the code to get the list from my database. I'm trying to update the quantity of the items in my list. Now there can be multiple items and quantity for those items needs to be updated accordingly. The problem I am facing is when I try to get the list and update item accordingly, before my for loop executes to update the item it doesn't update the item's quantity in the database and saves the order. What am I doing wrong?
I have used async functions, promises and flags to update the items quantity in the database but none helps.
This is my code for to get and update the item's quantity
const Express = require("express");
const app = Express.Router();
const Menu = require("../../models/Menu");
const Order = require("../../models/order");
const User = require("../../models/user");
app.post(
"/create",
async function(req, res) {
var myorder = {};
var orderList = [];
var ordDetail = [];
var UpdateMenus = [];
orderList = JSON.parse(JSON.stringify(req.body["OD"]));
if(orderList.length>0){
const user = await User.findOne({ _id: req.user.id })
.then(user => {
if (!user) {
return res.status(400).json({ error: "User Not Found" });
}
})
.then(() => {
var order = Order({
user: req.user.id
});
myorder = order;
(async function loop() {
for (i = 0; i < orderList.length; i++) {
const ordt = new Object({
menu: orderList[i]["menuId"],
order: myorder.id,
prize: orderList[i]["prize"],
quantity: orderList[i]["quantity"]
});
await Menu.findOne({ _id: orderList[i]["menuId"] })
.exec()
.then(menu => {
if (menu) {
if (menu.quantity >= ordt.quantity) {
menu.quantity = menu.quantity - ordt.quantity;
const editmenu = menu;
(async function updateTheMenu() {
await Menu.findOneAndUpdate(
{ _id: menu.id },
{ $set: editmenu },
{
new: true,
useFindAndModify: false
}
).then(updateMenu => {
console.log(updateMenu);
ordDetail.push(ordt);
});
})();
} else {
return res.status(400).json({
error:
menu.MenuText +
"" +
ordt.quantity +
" Qunatity Is Not Available"
});
}
}
});
}
})();
}).then(()=>{
order
.save()
.then(order => {
if (!order) {
return res.json({ error: "Order is not saved" });
}
res.status(200).json(order);
})
.catch(error => {
return res
.status(400)
.json({ error: "Fields are Not Correct" });
});
});
}
}
);
There are few things wrong with your code:
If you use await then you don't need to use then. You can just assign to a variable. Example:
const menu = await Menu.findOne({ _id: orderList[i]["menuId"] })
You don't need to wrap your loop and every await call in async functions. They are already in an async function.
You can write your response handler like this:
app.post('/create', async function(req, res) {
var myorder = {};
var orderList = [];
var ordDetail = [];
var UpdateMenus = [];
orderList = JSON.parse(JSON.stringify(req.body['OD']));
if (orderList.length > 0) {
const user = await User.findOne({ _id: req.user.id });
if (!user) {
return res.status(400).json({ error: 'User Not Found' });
}
var order = Order({
user: req.user.id
});
myorder = order;
for (i = 0; i < orderList.length; i++) {
const ordt = new Object({
menu: orderList[i]['menuId'],
order: myorder.id,
prize: orderList[i]['prize'],
quantity: orderList[i]['quantity']
});
const menu = await Menu.findOne({ _id: orderList[i]['menuId'] });
if (menu) {
if (menu.quantity >= ordt.quantity) {
menu.quantity = menu.quantity - ordt.quantity;
const editmenu = menu;
const updateMenu = await Menu.findOneAndUpdate(
{ _id: menu.id },
{ $set: editmenu },
{
new: true,
useFindAndModify: false
}
);
console.log(updateMenu);
ordDetail.push(ordt);
} else {
return res
.status(400)
.json({
error:
menu.MenuText +
'' +
ordt.quantity +
' Qunatity Is Not Available'
});
}
}
}
try {
const savedOrder = await order.save();
if (!savedOrder) {
return res.json({ error: 'Order is not saved' });
}
res.status(200).json(savedOrder);
} catch (error) {
return res.status(400).json({ error: 'Fields are Not Correct' });
}
}
});

what is the correct way to use async await with mariadb query in NodeJs?

I'm new to async/await.
I'm trying to use async and await but the query is not waiting and it happens at last and the page renders before the query so I can't get the correct answer on rendered page.
Here is my code before using async await
orderMiddleware.newOrder = function (req, res) {
var total = 0
var curr_total = 0
// get items from cart
c.query('select * from cart where user_id=:userId',
{ userId: req.user.ID }, function (err, cart) {
if (err) {
console.log(err)
} else {
cart.forEach(function (item) {
// Find item from DB and check their price
c.query('select * from products where id=:id',
{ id: item.item_id },
function (err, foundItem) {
if (err) {
console.log(err)
} else {
curr_total = foundItem[0].price * item.quantity
console.log("currenttotal" + curr_total)
total += curr_total
console.log(total)
}
})
})
console.log(total)
console.log(curr_total)
// Calculate total price
// Multiply all items with their quantity
res.render('orders/new', { cart: cart, total: total })
}
})
}
However this doesn't work properly. console.log(total) happens before the query so the result is zero and it renders zero in the rendered page.
Same thing happens if I use async. Am I using it wrong?
After using async await-
orderMiddleware.newOrder = async (req, res) => {
var total = 0
var curr_total = 0
// get items from cart
var A= c.query('select * from cart where user_id=:userId',
{ userId: req.user.ID }, async (err, cart) => {
if (err) {
console.log(err)
} else {
cart.forEach(async (item) => {
// Find item from DB and check their price
await c.query('select * from products where id=:id',
{ id: item.item_id },
async (err, foundItem) =>{
if (err) {
console.log(err)
} else {
curr_total = foundItem[0].price * item.quantity
console.log("currenttotal" + curr_total)
total += curr_total
console.log(total)
}
})
})
await console.log(total)
// await console.log(curr_total)
// Calculate total price
// Multiply all items with their quantity
await res.render('orders/new', { cart: cart, total: total })
}
})
}
I tried without using callbacks like:
var A= c.query('select * from cart where user_id=:userId',
{ userId: req.user.ID })
but then how can I get the output of the query?
console.log(A) shows different results.
You can't because the functions don't return promises. You can promisify those function using a thirty-part library (for example es6-promisify) or you can wrap those by yourself.
Once a function returns a Promise, you can await it.
For example, for the above, a solution could be the following:
const execQuery = (sql, params) => new Promise((resolve, reject) => {
query(sql, params, (error, data) => {
if (error) {
reject(error);
} else {
resolve(data);
}
});
});
const logCartItem = async (userId) => {
try {
const items = await execQuery('select * from cart where user_id=:userId', { userId });
items.forEach(console.log);
} catch (error) {
console.error(error);
}
};
Assuming you're using the node-mariasql package. Short answer is you can't use async/await because the package does not support Promises.
With node-mariasql it's easy to use promisify
const util = require('util')
const asyncQuery = util.promisify(c.query);
const rows = await asyncQuery.call(c, 'SELECT product FROM products WHERE id = :id', { id }, { useArray: false, metaData: false })

Resources