foreach loop in sync function in nodejs - node.js

I have code written
function getDetails (req, res) {
const dbQuery = `call spGetSLAReportsDetails('${req.body.domainId}', ${req.body.days},'${req.body.type}','${req.body.app}')`
try {
connectDatabase(dbQuery).then((rows) => {
if (!_.isEmpty(rows.dbData) && !_.isEmpty(rows.dbData[0])) {
const resultList = []
rows.dbData.pop()
var bar = new Promise((resolve, reject) => {
rows.dbData[0].forEach((element, index, array) => {
let query = `select * from YCW.YWFWIC ic where ic.witem=${element.witem} and ic.reqno=${element.reqno};`
connectDatabase(query).then((data) =>{
for (var i = 0; i < data.dbData.length; i++) {
element[data.dbData[i]["cfield"]] = data.dbData[i]["cvalue"]
}
resultList.push(element)
// console.log(resultList)
}).catch((err) => {
console.log(err)
})
if (index === array.length -1) resolve();
});
});
bar.then(() => {
console.log(resultList);
});
res.status(msgCodeJson.ERR004.code).send({
result: resultList })
} else {
console.log("empty array")
res.status(msgCodeJson.ERR004.code).send({
message : "No data found"
})
// httpResponseHandlerError(res, msgCodeJson.ERR001.code, msgCodeJson.ERR001.msg)
}
}).catch(() => {
httpResponseHandlerError(res, msgCodeJson.ERR002.code, msgCodeJson.ERR002.msg)
})
} catch (err) {
httpResponseHandlerError(res, msgCodeJson.ERR009.code, msgCodeJson.ERR009.msg)
}
}
module.exports.getDetails = getDetails
i want data to be fit in resultlist but i get empty list after all operation.
while in foreach loop i am getting proper output.
kindly help in issue.
i tried with async foreach loop but some syntax error is coming.
kindly help

as mentioned in the comment of the code you're using
Best way to wait for .forEach() to complete
This is OK if there is no async processing inside the loop.
yet you have an async function inside your forEach callback, namly this:
connectDatabase(query).then((data) => {
for (var i = 0; i < data.dbData.length; i++) {
element[data.dbData[i]["cfield"]] = data.dbData[i]["cvalue"]
}
resultList.push(element)
}).catch((err) => {
console.log(err)
})
you'll need to resolve the "outer/parent" promise from inside the "inner/child" promise
I suggest using a regular good old for loop and/or checking the count of resolved promises against the rows.dbData[0].length and calling a final code/function once they match

Related

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));
})
}

Saving with mongoose to mongodb in a loop

I want to save an object multiple times, with a change to the date field - adding one month per iteration.
A for loop doesn't work due to node async nature.
const saveMany = (req, res, obj, data) => {
let saves = [];
if (data.frequency === 'monthly') {
let i = 0;
for (let i = 0; i < data.reccuring_length; i++) {
const newEntry = new obj(data);
if (i != 0) newEntry.created_date.addMonths(1) //using datejs
newEntry.save((err, entry) => {
if (err) {
return res.status(400).send({
message: err
});
}
saves.push(entry);
})
}) //end of for loop
return res.json(saves)
} //end of if
}
I've seen stuff about promises / the async library but can't make a working implementation (I am new to this though so could be missing something obvious).
Any help is appreciated :)
EDIT:
Saving To MongoDB In A Loop
Found this link which is relevant, but if anyone has other suggestions that would be great.
EDIT 2:
Just realised my code has camelcase and snake case, changing in my code to make all object data snake case.
I think you can do somethings like that:
const saveMany = async (req, res, obj, data) => {
let saves = [];
if (data.frequency === 'monthly') {
let i = 0;
for (let i = 0; i < data.reccuring_length; i++) {
const newEntry = new obj(data);
if (i != 0) newEntry.created_date.addMonths(1) //using datejs
try{
const entry= await newEntry.save();
saves.push(entry);
} catch(err) {
return res.status(400).send({ message: err });
}
}) //end of for loop
return res.json(saves)
} //end of if
}

Knejx Js only promises

how to make this function work. Only a promise comes back to me.
codeProducts.forEach((code, index) => {
const qt = app.db('products').where('code',code).first().then(result => result.quantity)
data[index] = {
code: code,
quantity: qt
}
})
return data
There is two or three ways that you can solve this-
Approach: 1
I will make just one call to database.(I love my database :D) as-
let codeProducts = [55, 68, 96];
knex('products')
.whereIn('code', codeProducts)
.then((results) => {
// response to api call
console.log(results);
});
Approach: 2 (I don't like this approach. Too many call on db)
async function getData(codes) {
try {
let results = [];
for (let i = 0; i < codes.length; i++) {
let dbQuery = await knex('products').where('code', codes[i]).first();
results.push(dbQuery);
}
return results;
} catch (e) {
console.log(e);
}
}
const codeProducts = [54, 95];
getData()
.then((res) => {
console.log(res);
})
let data = codeProducts.map((code, index) => {
return app.db('products').where('code',code).first()
.then(result => {
return {
code: code,
quantity: result.quantity
}
})
})
return data
This code should fix your problem. You are accessing the quantity outside of the promise. In order to set the quantity on the data array, you need to do it inside the then

How to wait for an asynchronous process to complete inside a for loop before incrementing the loop

I need to iterate through an array. With each iteration, I need to update my database. I need to wait for the first update to be complete and then make the second update.
After searching through several answers, I found ASYNC/AWAIT feature of ES2017. However, I have not been able to implement it so far. The updates are happening randomly and not in a sequence. Please let me know how to implement ASYNC/AWAIT in this situation
Here is my code snippet:
function findRecipe(product, qty) {
return new Promise((resolve, reject) => {
Recipe.findOne({
product: product
}, (err, recipe) => {
if (err) {
reject(err)
} else {
for (let i = 0; i < recipe.items.length; i++) {
Item.findOne({
name: recipe.items[i].name
}, (err, item) => {
if (err) {
reject(err)
} else {
var lessAmt = recipe.quantities[i] * qty;
item.stock -= lessAmt;
item.save((err, item) => {
if (err) {
console.log(err)
} else {
resolve(item)
}
})
}
})
}
}
})
});
}
for (let i = 0; i < bill.product.length; i++) {
//Calling function for updates for each item
findRecipe(bill.product[i], bill.qty[i])
}
It looks like you are nearly there, Just Wrap the loop in a function and make it async.
async function updateAllRecipe(){
for(let i=0;i<bill.product.length;i++){
//Calling function for updates for each item
await findRecipe(bill.product[i],bill.qty[i])
}
}
But seriously though, I think you can leverage of parallelism here using the Promise.All. Is it really necessary to wait for the recipe to finish before queing the next findRecipe method? If not use the promise.all for it to perform faster
Async Await is easy to implement once you know the basic concept of asynchronous nature of Nodejs, I have used for...of loop here which also works asynchronously.
//Async function to perform database updates
async function findRecipe(product, qty) {
try {
let recipe = await Recipe.findOne({ product: product });
let i = 0;
for (itemObj of recipe.items) {
let item = await Item.findOne({ name: itemObj.name });
var lessAmt = recipe.quantities[i] * qty;
item.stock -= lessAmt;
let updatedItem = await item.save();
i++;
}
return true;
}
catch (err) {
return err;
}
}
async function someasyncFunction() {
for (let i = 0; i < bill.product.length; i++) {
//Calling function for updates for each item
await findRecipe(bill.product[i], bill.qty[i])
}
}
Use Promise.all and start process parallel. It'll increase the performance of API.
async function findRecipe(product, qty) {
try {
let recipe = await Recipe.findOne({
product: product
});
const items = await Promise.all(recipe.items.map(itemObj => Item.findOne({
name: itemObj.name
})));
const items = await Promise.all(items.map((item, i) => {
var lessAmt = recipe.quantities[i] * qty;
item.stock -= lessAmt;
return item.save();
}));
return true;
} catch (err) {
return err;
}
}
async function someasyncFunction() {
await Prmise.all(bill.product.map((product, i) => findRecipe(product, bill.qty[i])));
}

Promise inside async

I am writing a code using Node.js. I want to parse JSON array, retrieve elements from JSON array, make db call and assign values to JSON array. Make this complete operation in synchronous way. For this I wrote code using for loop:
for (let i = 0; i < items.length; i++) {
if(items[i].type === 'PickSimple'){
operation(item.searchSpec)
.then(lov => {
items[i].listOfValues = lov;
})
.catch(err =>{
console.log(err);
});
}
}
console.log("Final OBJ : "+items)
function operation(lov) {
return new Promise((resolve, reject) => {
Listofvalue.find({type: lov}, function(err, listofvalues) {
if (err) {
return reject(err);
}
return resolve(listofvalues);
});
});
But node is asynchronous, I am not getting desired result. So I have used async:
async.each(items,
function(item,callback) {
if(item.type === 'PickSimple'){
operation(item.searchSpec)
.then(lov => {
item.listOfValues = lov;
}).catch(err =>{
console.log(err);
});
}
}, err => {
if (err) console.error(err.message);
}
);
I have also tried using async.forEachOf.
Still I am not getting desired result. Is anything missing?
EDIT
async function processArr(items){
console.log("Inside processArr "+JSON.stringify(items));
for(const item in items){
console.log("Inside for loop, item : "+item);
if(item.type === 'PickSimple'){
var listOfValues = await operation(item.searchSpec)
item.listOfValues = listOfValues;
}
}
console.log("ProcessArr Final OBJ : "+JSON.stringify(items));
}
Output:
Inside processArr [{"name":"Call Related To","type":"PickSimple","searchSpec":"TM_CALL_RELATED_TO_SERVICE"},{"name":"Disposition Codes","type":"Text","searchSpec":""},{"name":"VOC 1","type":"Text","searchSpec":""}]
Inside for loop, item : 0
Inside for loop, item : 1
Inside for loop, item : 2
If you're running Node 8.x+ you can use the async/await. The following for...in should await for promise to complete before iterating to next item.
PS. I've not tested that method, let me know if it works for you.
async function processArr(items){
for(const item in items){
if(items[item].type === 'PickSimple'){
var listOfValues = await operation(items[item].searchSpec)
items[item].listOfValues = listOfValues;
}
}
console.log("Final OBJ : "+items)
}
EDIT:
You're getting undefined because you're calling console.log inside console.log.

Resources