Promise inside async - node.js

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.

Related

foreach loop in sync function in nodejs

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

Wait for then() to finish

I have been having a problem waiting for one part of my code to finish for me to run the other part,
How can part 1 finish before part 2?
var uidArray = [];
// Part 1
admin.auth().listUsers(1000).then(result => {
result.users.forEach(userRecord => {
uidArray.push(userRecord.uid);
console.log("pushing to uid array user " + userRecord.uid);
});
return;
}).catch(error => {
console.log("Error listing users:" + error);
});
uidArray.forEach(uid => {
// Part 2 of the code
});
You can use async-await for better data manipulation. However, forEach is by default asynchronous it can't wait until for each completes you can use below using async-await with try-catch block.
try{
var uidArray = [];
const result = await admin.auth().listUsers(1000);
for(const userRecord of result.users) {
uidArray.push(userRecord.uid);
console.log("pushing to uid array user " + userRecord.uid);
}
uidArray.forEach(uid => {
// Part 2 of the code
});
} catch(err) {
console.log(err);
}
var uidArray = [];
// Part 1
admin.auth().listUsers(1000).then(result => {
result.users.forEach(userRecord => {
uidArray.push(userRecord.uid);
console.log("pushing to uid array user " + userRecord.uid);
});
return;
}).catch(error => {
console.log("Error listing users:" + error);
}).then(()=>{
uidArray.forEach(uid => {
// Part 2 of the code
});
});
The key here is that after you start a promise, you can keep thening off it. Ideally you should also return a promise after each step, but it's not required.
This link has more information
https://javascript.info/promise-chaining
You can use below code using async-await as it is good for data manipulation.i prefer for loop instead using forEach.
async function test(){
try{
let result = await admin.auth().listUsers(1000);
let usersArray = result.users;
var uidArray = usersArray.map(function(item) { return item.uid; });
// here you can loop through uidArrar either for or forEach whatever you want
for(let i =0 ; i< uidArray.length; i++){
// your code to be executed in second part
}
} catch (e) {
//error
}
}
test()

Wait for foreach to complete NodeJS

So, I am creating an API in NodeJS. In one API, I have to call for loop within mongoose query.
How do I wait for the forEach to complete before executing res.send()? I have attached my code below.
router.post("/getResult", function (req, res) {
const lottery_id = req.body.lottery_id;
const prizeQuery = Prize.find({"lotid": lottery_id});
let response = [];
prizeQuery.exec(function (err, prizes) {
console.log(prizes.length);
if (err) return res.send({success: 0, data: err});
else {
prizes.forEach(prize => {
let prizeData = {};
const winnerQuery = Winner.find({"id": prize._id});
winnerQuery.exec(function (err, winners) {
if (err) return res.send({success: 0, data: err});
else {
prizeData = {
prize: prize,
winners: winners
};
response.push(prizeData);
}
});
});
}
});
return res.send({success:1, data: response});
});
In the code above, return is called before forEach is completed.
Because you are running asynchronous code inside the forEach and forEach will not wait until the asynchronous code finish, to do so, you must wrap your async code with a waiting primitive.
Also the code you provided will call send twice in case of failure, because the return inside the forEach will not actually end the enclosing function.
try {
await Promise.all(prizes.map(async (prize) => {
const winners = await Winner.find({"id": prize._id});
response.push({prize, winners});
}))
res.send({success:1, data: response});
} catch (err) {
res.send({success: 0, data: err});
}
#Rami's answer is correct but addition to that you can also use forof for your code to work. forof works synchronously so there will be no need for promise. like
for (let prize of prizes){
let prizeData = {};
try{
const winnerQuery = Winner.find({"id": prize._id});
const winners = await winnerQuery.exec()
prizeData = {
prize: prize,
winners: winners
};
response.push(prizeData);
}catch(err){
console.error(err)
}
}

async save multiple document with mongoose

i am updating 2 documents using mongoose.save(), but i think the way I am doing is is not safe, as far as i know i need to use async to make sure all documents are being executed
// array containing the 2 documents from db
let schedules
let newItem = {
isActive: room.isActive,
name: room.roomname
};
// adding new items to nested array
schedules[0].rooms.push(newItem);
schedules[1].rooms.push(newItem);
// saving / updating documents
var total = schedules.length,
result = [];
function saveAll() {
var doc = schedules.pop();
doc.save(function(err, saved) {
if (err) throw err; //handle error
result.push(saved);
if (--total) saveAll();
else {
// all saved here
res.json(result);
}
});
}
saveAll();
any explanation how to do it correctly
We can use promise.all for this but we need to change your save function to promise based function
...
var total = schedules.length,
result = [];
function saveAll() {
const promises = schedules.map(schedule => save(schedule));
return Promise.all(promises)
.then(responses => {
// all saved processes are finished
res.json(responses);
})
}
// convert callback `save` function to promise based
function save(doc) {
return new Promise((resolve, reject) => {
doc.save((err, saved) => {
if (err) {
reject(err);
}
resolve(saved);
});
});
}
If you can use async/await we can make saveAll function cleaner
async function saveAll() {
const promises = schedules.map(schedule => save(schedule));
const responses = await Promise.all(promises);
res.json(responses);
}
Hope it helps
Use Promises :
doc1.save().exec().then(
data => {
doc1.save().exec().then(
data2 => console.log(data2)
).catch(err2 => console.log(err2))
}
).catch(err1 => console.log(err1))

Call synchronously recursively function in nodejs

I'm developing MEANJS project. Here a function which return array by fetch data recursively. Now, I have the problem that how to get that array.
Problem:-
let users A have properties is {_id:'001',rmUserId:'001'}, B:{_id:'002',rmUserId:'001'}, C {_id:'003',rmUserId:'002'}, D {_id:'003',rmUserId:'001'}, E {_id:'004',rmUserId:'009'}
if user A will login then allUnderUsers array have B,C,D users. That means all users have to follow an own hierarchy.
A
/ \
B D
/
C
Here is my code:-
module.exports.getUnderUsersByRm = function(currentUser, callback) {
try {
var allUnderUsers = [];
function __getUserByRmId(rmId) {
User.find({ rmUserId: rmId, isAlive: true, status: 'active' })
.exec(function(err, users) {
if (err)
return callback(err)
if (users.length > 0) {
users.forEach(function(ele, i) {
allUnderUsers.push(ele);
__getUserByRmId(ele.rmUserId);
});
} else {
return false;
}
})
}
__getUserByRmId(currentUser._id);
} catch (e) {
callback(e)
}
}
Here I need to get allUnderUsers array after all recursive function called.
I have use callback function like:-
....
...
__getUserByRmId(currentUser._id);
callback(null,'done');
.
.
but it throws an error i.e,
Error: Can't set headers after they are sent
.at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:346:11)
at ServerResponse.header (/home/clsah/projects/LMS/node_modules/express/lib/response.js:719:10)
........ .......
If you take advantage of the promises built into the later versions of mongoose and surface a promise interface from your module, you can do this:
Simulated running code here: https://jsfiddle.net/jfriend00/zr6ynmsu/
module.exports.getUnderUsersByRm = function(currentUser) {
function __getUserByRmId(rmId) {
// return promise here
return User.find({ rmUserId: rmId, isAlive: true, status: 'active' }).exec().then(function(users) {
if (users.length > 0) {
let promises = [];
users.forEach(function(ele, i) {
promises.push(__getUserByRmId(ele.rmUserId));
});
// return promise which will chain it to original promise
// this is the key to getting the master promise to wait
// for everything to be done
return Promise.all(promises).then(results => {
// add in previous results
// flatten all the results together into a single array
// and remove empty results
results.unshift(users);
return [].concat.apply([], results.filter(item => item.length > 0));
});
} else {
return [];
}
});
}
return __getUserByRmId(currentUser);
}
And, then you would use it like this:
const someModule = require('yourModule');
someModule.getUnderUsersByRm(someUser).then(results => {
// process all results here
}).catch(err => {
// error here
});
If you still want your callback interface on getUnderUsersByRm, you can still do that (though if you're doing more than a few async calls, it really is worth using promises for all async operations):
module.exports.getUnderUsersByRm = function(currentUser, callback) {
function __getUserByRmId(rmId) {
// return promise here
return User.find({ rmUserId: rmId, isAlive: true, status: 'active' }).exec().then(function(users) {
if (users.length > 0) {
let promises = [];
users.forEach(function(ele, i) {
promises.push(__getUserByRmId(ele.rmUserId));
});
// return promise which will chain it to original promise
// this is the key to getting the master promise to wait
// for everything to be done
return Promise.all(promises).then(results => {
// add in previous results
// flatten all the results together into a single array
// and remove empty results
results.unshift(users);
return [].concat.apply([], results.filter(item => item.length > 0));
});
} else {
return [];
}
});
}
__getUserByRmId(currentUser).then(result => {
callback(null, result);
}).catch(err => {
callback(err);
});
}
If your user tree is circular, then you can protect against an infinite loop by keeping track of all the visited users. You need some sort of unique key that identifies each user. Since I don't know what that is in your program, I will assume the user you are passing in is already an id. Any property that uniquely identifies the user will work in this scheme:
module.exports.getUnderUsersByRm = function(currentUser) {
let visitedUsers = new Set();
function __getUserByRmId(rmId) {
// return promise here
return User.find({ rmUserId: rmId, isAlive: true, status: 'active' }).exec().then(function(users) {
if (users.length > 0) {
let promises = [];
users.forEach(function(ele, i) {
// make sure we aren't already processing this user
// avoid circular loop
let userId = ele.rmUserId;
if (!visitedUsers.has(userId)) {
visitedUsers.add(userId);
promises.push(__getUserByRmId(userId));
}
});
// return promise which will chain it to original promise
// this is the key to getting the master promise to wait
// for everything to be done
return Promise.all(promises).then(results => {
// add in previous results
// flatten all the results together into a single array
// and remove empty results
results.unshift(users);
return [].concat.apply([], results.filter(item => item.length > 0));
});
} else {
return [];
}
});
}
return __getUserByRmId(currentUser);
}
I tried to implement the solution using async based approach. You may find it naive, but it should work.
function __getUserByRmId(rmId, cb) {
var allUnderUsers = [];
User.find({ rmUserId: rmId, isAlive: true, status: 'active' })
.exec(function(err, users) {
async.each(users, function(user, callback){
if (user._id != rmId){
// recursive call
__getUserByRmId(user._id, function(childUsers){
childUsers.forEach(function (childUser) {
allUnderUsers.push(childUser);
});
callback(); //intermediate callback for async call
});
} else { //condition check to avoid infinite loop
allUnderUsers.push(user);
callback(); //intermediate callback for-loop
}
}, function(err){
cb(allUnderUsers); //final callback with result
});
});
}
module.exports.getUnderUsersByRm = function(currentUser, callback) {
__getUserByRmId(currentUser._id, callback)
};
Logically, it should work. Please give a try and let me know, if there are any issue. For now, it returns array containing parent as well. e.g. [A, B, C, D] for your example.
Use async.until and maintain an array of element to be processed
var async = require('async');
module.exports.getUnderUsersByRm = function(currentUser, callback) {
try {
var allUnderUsers = [];
var usersToProcess = [currentUser._id]; // Array to track what was earlier done with recursion
async.until(function() { // Test function for async.until
return usersToProcess.length === 0;
}, function __getUserByRmId(callback2) { // fn for async.until
User.find({
rmUserId: usersToProcess.shift(), // Take first element of array
isAlive: true,
status: 'active'
})
.exec(function(err, users) {
if (err)
return callback2(err)
if (users.length > 0) {
users.forEach(function(ele, i) {
allUnderUsers.push(ele);
usersToProcess.push(ele.rmUserId);
// __getUserByRmId(ele.rmUserId); // To Remove
});
return callback2(); // Always call callback;
} else {
return callback2(); // Earlier: return false; Return some err argument if you want
}
})
}, callback); // Final callback for async.until
} catch (e) {
callback(e);
}
}
I'm pretty sure that you are getting that error because you run __getUserByRmId function (which inside calls the callback function if an error occurs, and then, besides anything that happen you call the callback function again, which may be sending multiple responses to one same request, and so on, setting headers multiple times even when the response was already sent.
I should have post this in the comments but don't have enough reputation to do so

Resources