Node - Nested For loop async behaviour - node.js

I am trying to create Dynamic JSON from database but due to async behaviour of node js I am not able to do that.
exports.wsGetDirectoryTree = function(request,response){
pool.getConnection(function(err, connection) {
if (err) {
console.log("Error while connecting to DB : " + err);
response.send(error);
return;
} else {
// Org List
objDb.wsGetOrganisationTree(connection, function(callback) {
if(callback){
var data= JSON.stringify(callback);
var jsonObject = JSON.parse(data);
var count = Object.keys(jsonObject).length;
var finalResponse = '';
var org = '';
var gst = '';
var url = '';
// Org List
for(var i=0;i<count;i++){
var temp = JSON.stringify(jsonObject[i]);
var tempData = JSON.parse(temp);
(function(i){
// {"Apple"
org = '{'+'\"'+tempData.organisation_name+'\":';
console.log("org -> "+org);
// Guest list
objDb.wsGetGuestTree(tempData.organisation_id,connection, function(callback) {
if(callback){
var data= JSON.stringify(callback);
var jsonObject = JSON.parse(data);
var count = Object.keys(jsonObject).length;
// Guest list
for(var j=0;j<count;j++){
var temp = JSON.stringify(jsonObject[j]);
var tempData = JSON.parse(temp);
//{"Jhon":
gst = '{'+'\"'+tempData.guest_name+'\":';
console.log("gst = "+gst);
finalResponse = org + gst;
// Url list
objDb.wsGetUrlTree(tempData.guest_id,connection, function(callback) {
if(callback){
var data= JSON.stringify(callback);
finalResponse = finalResponse + data;
console.log("finalResponse = "+finalResponse);
return;
} else {
return;
}
});
}
data = data.replace('[','');
data = data.replace(']','');
return;
} else {
return;
}
});
})(i); //function for counting
} //loop end
response.send(data);
return;
} else {
response.send(invalidData);
return;
}
});
}
connection.release();
});
};
I am expecting below output -
{
"Organisation01": {
"Guest01": {
"Images": ["IMG-20180117-WA0004.jpg", "IMG-20180117-WA0004.jpg"],
"Video": ["IMG-20180117-WA0004.jpg", "IMG-20180117-WA0004.jpg"]
},
"Guest02": {
"Images": ["IMG-20180117-WA0004.jpg", "IMG-20180117-WA0004.jpg"]
}
}, "Organisation02": {
"Guest01": {
"Images": ["IMG-20180117-WA0004.jpg", "IMG-20180117-WA0004.jpg"]
} }, "Organisation03": {
"Guest01": {
"Images": ["IMG-20180117-WA0004.jpg", "IMG-20180117-WA0004.jpg"]
} }, "Organisation04": {
"Guest01": {
"Images": ["IMG-20180117-WA0004.jpg", "IMG-20180117-WA0004.jpg"]
} } }

Since your for loop over count actually requires to make async calls objDb.wsGetGuestTree() , it would be advisable to use async module.
Please check :
https://www.npmjs.com/package/async
https://caolan.github.io/async/docs.html#eachSeries

Related

Parent function is not waiting for child to finish

Inside getMetadata(), Object.keys function is not waiting for httprequest to finish. How can I make object.keys function to wait till httprequest function manipluates the result variable?
I'm using node. I tried to make promise but failed.
function fetchAirportPageIDsListWithMetaJSON(faa, cb){
logger.info('[airportcms-data-processor] fetching airport pages list with Metadata');
var faa = faa
async.waterfall([
getAirportPageIDsList,
getMetadata,
], function (err, result) {
cb(null, result);
});
function getAirportPageIDsList(callback) {
httpRequests.fetchData('//s3 url to fetch data', function (err, data) {
var idsMap={};
data["page-ids"].forEach( (obj) => {
obj.list.forEach((item) => idsMap[item] = obj.id);
});
callback(null, idsMap);
})
}
function getMetadata(data,callback) {
var result=[];
Object.keys(data).sort().forEach( function (t) {
var tempJson={};
var urlcheck = verifySpecialPageId(t);
if (urlcheck){
var url = config.urls.s3_airports_base_url+'/'+faa+'/'+urlcheck;
}else{
var url = config.urls.s3_airports_base_url+'/'+faa+'/'+t;
}
tempJson["sectionId"]= t;
tempJson["page"]= data[t];
httpRequests.makeHeadRequestWithCallerId(url, function (err, metax) {
if (metax){
let z = metax.split('|')[0];
tempJson["SummaryRange"]= getSummaryRangeAirportPageList(z);
tempJson["timestamp"]= new Date(parseInt(z)).toLocaleDateString();
tempJson["callerId"]= metax.split('|')[1];
}else{
tempJson["timestamp"]='';
tempJson["callerId"]='';
tempJson["SummaryRange"]='';
}
})
result.push(tempJson);
});
logger.info("Final result: ", result);
callback(null, result);
}
}
http request function:
function makeHeadRequestWithCallerId (url, cb) {
httpRequest.head(url, function (err, res) {
if (err) {
logger.error('Error ' + err);
return cb(err, null);
}
if(res.code === 200) {
if (res.headers['x-amz-meta-x-amz-meta-lastmodified'] || res.headers['x-amz-meta-x-amz-meta-callerid']) {
var dateModified = res.headers['x-amz-meta-x-amz-meta-lastmodified'];
var timeStamp = Date.parse(dateModified);
var callerid = res.headers['x-amz-meta-x-amz-meta-callerid'];
if(timeStamp && callerid) {
return cb(null, timeStamp+'|'+callerid);
} else if (callerid){
return cb(null, '|'+callerid);
}else if(timeStamp){
return cb(null, timeStamp+'|');
}else{
return cb(null, null);
}
}else{
return cb(null, null);
}
}
});
}
Current log=> Final result:
[{ sectionId: 'internet-wifi', page: 'internet-wifi' },
{ sectionId: 'layover-ideas', page: 'layover-ideas' }]
Expected log=> Final result:
{ sectionId: 'internet-wifi',
page: 'internet-wifi',
SummaryRange: '12-99',
timestamp: '1/29/2018',
callerId: '' },
{ sectionId: 'layover-ideas',
page: 'layover-ideas',
SummaryRange: '12-99',
timestamp: '1/26/2017',
callerId: '' },
function getMetadata(data, callback) {
var result = [];
var count = Object.keys(data).length;
var i = 0;
Object.keys(data).sort().forEach(function (t) {
var tempJson = {};
var urlcheck = verifySpecialPageId(t);
if (urlcheck) {
var url = config.urls.s3_airports_base_url + '/' + faa + '/' + urlcheck;
} else {
var url = config.urls.s3_airports_base_url + '/' + faa + '/' + t;
}
tempJson["sectionId"] = t;
tempJson["page"] = data[t];
httpRequests.makeHeadRequestWithCallerId(url, function (err, metax) {
if (metax) {
let z = metax.split('|')[0];
tempJson["SummaryRange"] = getSummaryRangeAirportPageList(z);
tempJson["timestamp"] = new Date(parseInt(z)).toLocaleDateString();
tempJson["callerId"] = metax.split('|')[1];
} else {
tempJson["timestamp"] = '';
tempJson["callerId"] = '';
tempJson["SummaryRange"] = '';
}
result.push(tempJson);
i++;
if(count === i){
logger.info("Final result: ", result);
callback(null, result);
}
})
});
}

How to send a response only after a query has been executed in loopback

I have a remote method in loopback like:
Alerts.getAlertDetails = function (alertId, options, cb) {
var response = {};
var userId = options.accessToken.userId;
Alerts.app.models.MobileUserAlertRelation.find({where: {userId: userId, alertId: alertId, isDeleted: -1}, include: {relation: 'alerts', scope: {include: ['alertTypes'], where: {status: 1}}}}, function (err, alertRel) {
if (alertRel.length > 0 && alertRel[0].alerts()) {
response.code = 200;
response.status = "success";
response.data = {};
if (alertRel[0].alertId) {
response.data.alertId = alertRel[0].alertId;
}
if (alertRel[0].readStatus) {
response.data.readStatus = alertRel[0].readStatus;
}
if (alertRel[0].receivedOn) {
response.data.alertReceivedOn = alertRel[0].receivedOn;
}
var alertData = alertRel[0].alerts();
if (alertData.title) {
response.data.alertTitle = alertData.title;
}
if (alertData.message) {
response.data.alertShortMessage = alertData.message;
}
if (alertData.extraMessage) {
response.data.alertMessage = alertData.extraMessage;
}
if (alertData.priority) {
response.data.alertPriority = alertData.priority;
}
if (alertData.validUntil) {
response.data.alertExpiresOn = alertData.validUntil;
}
if (alertData.images && alertData.images.length > 0) {
response.data.alertImages = [];
for (var image in alertData.images) {
if (alertData.images.hasOwnProperty(image)) {
response.data.alertImages.push(constants.ALERT_IMAGE_URL + '/' + alertData.images[image]);
}
}
}
if (alertData.alertTypes() && alertData.alertTypes().alertTypeName) {
response.data.alertType = alertData.alertTypes().alertTypeName;
}
if (alertData.alertLocations && alertData.alertLocations > 0) {
response.data.alertLocations = [];
response.data.policeDepartments = [];
response.data.hospitals = [];
response.data.fireDepartments = [];
var locations = alertData.alertLocations;
for (var locKey in locations) {
if (locations.hasOwnProperty(locKey)) {
if (locations[locKey].data) {
response.data.alertLocations.push(locations[locKey].data);
console.log(locations[locKey].data);
if (locations[locKey].data.type) {
var locationType = locations[locKey].data.type;
if (locationType === "Polygon") {
var coordinates = locations[locKey].data.coordinates[0];
var polygonCenter = getPolygonCenter(coordinates);
console.log(polygonCenter);
}
}
}
}
}
}
cb(null, response);
} else {
response.code = 404;
response.status = 'error';
response.message = 'Alert not found.';
cb(null, response);
}
})
};
But when I call this method through api, response is received without data added from the complex code part. I know that callback will be called asynchronously here and so that cb(response) will be called before the complex code is executed completely. How can i send response only after the complex part is completed and data is correctly added to response from that data. I cannot move cb(response) inside the complex part as data is being pushed in for loop.
I have heard of promises, can it be used here, if so, how could it be done?
Someone please help!!
The problem is because of fetching relation in if.
The relation method is an async.
Alerts.getAlertDetails = function (alertId, options, cb) {
var response = {};
var userId = options.accessToken.userId;
Alerts.app.models.MobileUserAlertRelation.find({where: {userId: userId, alertId: alertId, isDeleted: -1}, include: {relation: 'alerts', scope: {include: ['alertTypes'], where: {status: 1}}}}, function (err, alertRel) {
if(alertRel.length < 1){
return handleError();
}
alertRel[0].alerts(handleResponse);
function handleResponse(err, alertRelAlert){
if(err) return handleError();
if (alertRelAlert) {
//all that code in question if if section
}else {
return handleError();
}
}
function handleError(){
response.code = 404;
response.status = 'error';
response.message = 'Alert not found.';
cb(null, response);
}
});
}

Completing loops inside of node.js promises

I am trying to complete a few loops over firebase objects using .forEach and I am also using promises. This isn't working out how I had planned it. My basic problem is that the loops inside of my promises complete well after the promise chain itself completes. Here is my function:
var queue = new Queue(productUpdateQueue, function(data, progress, resolve, reject) {
var incomingUpdateData = data;
var receiptID = incomingUpdateData.receiptID;
var userID = incomingUpdateData.userID;
var oldProductID = incomingUpdateData.oldProductID;
var newProductID = incomingUpdateData.newProductID;
var newReceipt = incomingUpdateData.newReceipt;
var postID = "";
var updateObject = {};
updateObject['usersPrivate/'+userID+'/receipts/'+receiptID+'/items/'+oldProductID] = null;
updateObject['usersPrivate/'+userID+'/receipts/'+receiptID+'/items/'+newProductID] = newReceipt;
clicks.child('VigLink').orderByChild('item').equalTo(oldProductID).once('value', function(cuidSnapshot) {
return cuidSnapshot.forEach(function(cuidSnapshot) {
var cuid = cuidSnapshot.key;
updateObject['clicks/VigLink/'+cuid+'/item'] = newProductID;
console.log('one');
progress(20);
});
}).then(function() {
return userReceiptMetrics.child(userID).child('receipts').child(receiptID).child('items').child(oldProductID).once('value', function(oldSnapshot) {
var data = oldSnapshot.val()
updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/items/'+oldProductID] = null
updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/items/'+newProductID] = data
if (data != null) {
updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/itemIDs/'+newProductID] = now
updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/itemIDs/'+oldProductID] = null
};
console.log('two');
progress(40);
});
}).then(function() {
return userReceiptMetrics.child(userID).child('shops').child(oldProductID).once('value', function(oldSnapshot) {
var data = oldSnapshot.val()
updateObject['userReceiptMetrics/'+userID+'/shops/'+oldProductID] = null;
updateObject['userReceiptMetrics/'+userID+'/shops/'+newProductID] = data;
if (data != null) {
updateObject['userReceiptMetrics/'+userID+'/shopIDs/'+newProductID] = now;
updateObject['userReceiptMetrics/'+userID+'/shopIDs/'+oldProductID] = null;
};
console.log('three');
progress(60);
});
}).then(function() {
posts.once('value', function(postSnapshot) {
// use Promise.all and Array#map to wait for all these queries to finish
var allPosts = postSnapshot.val()
var postKeys = Object.keys(allPosts)
return Promise.all(postKeys.map(function(postKey) {
var postID = postKey;
return posts.child(postID).child('items').child(oldProductID).once('value', function(itemSnapshot) {
return itemSnapshot.forEach(function(itemSnapshot) {
var itemData = itemSnapshot.val()
console.log('post snapshot'+ itemData);
updateObject['posts/'+postID+'/items/'+oldProductID] = null
updateObject['posts/'+postID+'/items/'+newProductID] = itemData
});
});
}));
});
}).then(function() {
// Move to next item
return console.log('hey look here'+updateObject['posts/'+postID+'/items/'+newProductID]);
return firebaseRoot.update(updateObject, function(error) {
if (error) {
console.log("Error updating data:", error);
reject()
} else {
progress(100);
// resolve();
console.log('four');
}
});
}).then(function() {
// Move to next item
return console.log('second one'+updateObject['posts/'+postID+'/items/'+newProductID]);
return firebaseRoot.update(updateObject, function(error) {
if (error) {
console.log("Error updating data:", error);
reject()
} else {
progress(100);
// resolve();
console.log('four');
}
});
});
// Finish the task asynchronously
setTimeout(function() {
reject();
}, 10000);
});
And here is the output:
one
two
three
hey look hereundefined
second oneundefined
post snapshot[object Object]
post snapshot[object Object]
post snapshot[object Object]
post snapshot[object Object]
post snapshot[object Object]
post snapshot[object Object]
post snapshot[object Object]
post snapshot[object Object]
I think I might be using promises incorrectly but I don't know.
I figured out that I should be using Promise.all() in order to wait for my forEach loops to complete. Here is the code I used to solve my problem, enjoy:
var queue = new Queue(productUpdateQueue, function(data, progress, resolve, reject) {
var incomingUpdateData = data;
var receiptID = incomingUpdateData.receiptID;
var userID = incomingUpdateData.userID;
var oldProductID = incomingUpdateData.oldProductID;
var newProductID = incomingUpdateData.newProductID;
var newReceipt = incomingUpdateData.newReceipt;
var postID = "-KZOO0UII67uOmYo6DJh";
var postKeys = [];
var updateObject = {};
updateObject['usersPrivate/'+userID+'/receipts/'+receiptID+'/items/'+oldProductID] = null;
updateObject['usersPrivate/'+userID+'/receipts/'+receiptID+'/items/'+newProductID] = newReceipt;
return clicks.child('VigLink').orderByChild('item').equalTo(oldProductID).once('value', function(cuidSnapshot) {
return cuidSnapshot.forEach(function(cuidSnapshot) {
var cuid = cuidSnapshot.key;
updateObject['clicks/VigLink/'+cuid+'/item'] = newProductID;
progress(10);
});
}).then(function() {
return userReceiptMetrics.child(userID).child('receipts').child(receiptID).child('items').child(oldProductID).once('value', function(oldSnapshot) {
var data = oldSnapshot.val()
updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/items/'+oldProductID] = null
updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/items/'+newProductID] = data
if (data != null) {
updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/itemIDs/'+newProductID] = now
updateObject['userReceiptMetrics/'+userID+'/receipts/'+receiptID+'/itemIDs/'+oldProductID] = null
};
progress(25);
});
}).then(function() {
return userReceiptMetrics.child(userID).child('shops').child(oldProductID).once('value', function(oldSnapshot) {
var data = oldSnapshot.val()
updateObject['userReceiptMetrics/'+userID+'/shops/'+oldProductID] = null;
updateObject['userReceiptMetrics/'+userID+'/shops/'+newProductID] = data;
if (data != null) {
updateObject['userReceiptMetrics/'+userID+'/shopIDs/'+newProductID] = now;
updateObject['userReceiptMetrics/'+userID+'/shopIDs/'+oldProductID] = null;
};
progress(40);
});
}).then(function() {
progress(55);
return posts.orderByChild('receipt').equalTo(receiptID).once('value');
}).then(function(postSnapshot) {
return postSnapshot.forEach(function(post) {
progress(70);
postKeys.push(post.key)
});
}).then(function() {
return Promise.all(postKeys.map(function(postKey) {
return posts.child(postKey).child('items').child(oldProductID).once('value', function(itemSnapshot) {
var itemData = itemSnapshot.val()
updateObject['posts/'+postKey+'/items/'+oldProductID] = null;
updateObject['posts/'+postKey+'/items/'+newProductID] = itemData;
});
})).then(function(results) {
progress(85);
return results;
});
}).then(function() {
return firebaseRoot.update(updateObject, function(error) {
if (error) {
console.log("Error updating data:", error);
reject()
} else {
progress(100);
resolve();
}
});
});
// Finish the task asynchronously
setTimeout(function() {
reject();
}, 10000);
});

Waiting for asynchronous task to complete nodejs

I am creating an application where a user can have many rooms and each room can have many channels, here is my code when retrieving the rooms and corresponding channels:
getRooms: function (req, res) {
User.find({id: req.cookies.claver_id}).exec(function (err, result) {
if (err) {
return res.send(400);
}
rooms = result[0].rooms;
if (rooms.length === 1) {//No room defaults to ['']
return res.send(400);
}
var roomsObj = {};
var roomsArr = [];//we will place the roomsObj inside the roomsArr
var chansObj = {};
var chansArr = [];
async.each(rooms, function (roomId, cb){
roomsObj = {};
if (roomId !== '') {
Rooms.findOne({id: roomId}).exec(function (err, room){
roomName = room.name;
inviteLink = room.inviteLink;
roomsObj.name = roomName;
roomsObj.id = roomId;
roomsObj.inviteLink = inviteLink;
var channels = room.channels;
async.each(channels, function (channelId, cb) {
chansObj = {};
Channels.findOne({id: channelId}).exec(function (err, channel){
chansObj.name = channel.channelName;
chansObj.id = channelId;
chansObj.type = channel.channelType;
chansArr.push(chansObj);
cb();
});
},
function (err) {
});
});
}
cb();
}, function (err) {
roomsObj.channels = chansArr;
roomsArr.push(roomsObj);
sails.log(roomsArr);
});
});
}
It is suppose to return a javascript object with the following structure:
[ { name: "Room Name",
roomId: "Room Id",
inviteLink: "Room Invite Link",
channels: [
{
name: "Channel Name",
id: "channel Id"
}
]
}
]
But I always get an empty array because async.each(rooms, function (roomId, cb){ }) does not wait for async.each(channels, function (channelId, cb) {}) to complete, so I have empty room object. Please how do I solve this issue ?
You should call your rooms's callback loop after completing you channels loop.
You should do something like this:
getRooms: function (req, res) {
User.find({id: req.cookies.claver_id}).exec(function (err, result) {
if (err) {
return res.send(400);
}
rooms = result[0].rooms;
if (rooms.length === 1) {//No room defaults to ['']
return res.send(400);
}
var roomsObj = {};
var roomsArr = [];//we will place the roomsObj inside the roomsArr
var chansObj = {};
var chansArr = [];
async.each(rooms, function (roomId, callback1){
roomsObj = {};
if (roomId !== '') {
Rooms.findOne({id: roomId}).exec(function (err, room){
roomName = room.name;
inviteLink = room.inviteLink;
roomsObj.name = roomName;
roomsObj.id = roomId;
roomsObj.inviteLink = inviteLink;
var channels = room.channels;
var i=0;
async.each(channels, function (channelId, callback2) {
chansObj = {};
Channels.findOne({id: channelId}).exec(function (err, channel){
chansObj.name = channel.channelName;
chansObj.id = channelId;
chansObj.type = channel.channelType;
chansArr.push(chansObj);
i++;
if(i===(channels.length-1)){
i=0;
callback1();
}else{
callback2();
}
});
},
function (err) {
});
});
}
}, function (err) {
roomsObj.channels = chansArr;
roomsArr.push(roomsObj);
sails.log(roomsArr);
});
});
}
I solved it, it really was a case for promises, I used bluebird promise combined with async - the modified code:
getRooms: function (req, res) {
User.find({id: req.cookies.claver_id}).exec(function (err, result) {
if (err) {
return res.send(400);
}
rooms = result[0].rooms;
if (rooms.length === 1) {//No room defaults to ['']
return res.send(400);
}
var roomsObj = {};
var roomsArr = [];//we will place the roomsObj inside the roomsArr
var chansObj = {};
var chansArr = [];
Promise.each(rooms, function (roomId, callback1){
roomsObj = {};
if (roomId !== '') {
async.series ([
function () {
Rooms.findOne({id: roomId}).then(function (room){
roomName = room.name;
inviteLink = room.inviteLink;
roomsObj.name = roomName;
roomsObj.id = roomId;
roomsObj.inviteLink = inviteLink;
channels = room.channels;
sails.log(roomName);
})
}
]);
return Promise.each(channels, function (channelId) {
return Promise.all([
Channels.findOne({id: channelId}).then(function (channel){
chansObj = {};
chansObj.name = channel.channelName;
chansObj.id = channelId;
chansObj.type = channel.channelType;
chansArr.push(chansObj);
sails.log(chansObj);
})
]).then(function () {
sails.log('done one');
});
}).then(function () {
roomsObj.channels = chansArr;
roomsArr.push(roomsObj);
sails.log('done all');
chansArr = [];
});
}
}).then(function () {
sails.log(roomsArr);
sails.log("grand finish");
});
});
}
Thanks to everyone who contributed.

Loop callback in async.waterfall node js

I want to execute the callback in the else of my each loop. In my console i have the "Found" written but the callback isn't executed ...
async.waterfall([
function readFile(callback){
console.log("Start async");
var params = {Bucket : "MyBucket", Key: "MyKey"};
reads3.getObject(params, function extract(err,data) {
//read a json object
console.log("Start reading");
callback(err,data);
});
},
function(data, callback){
var content = data.Body.toString('utf-8').trim();
var jsonparse = JSON.parse(content);
async.each(config, function(item) {
var currentPath = item.path;
if((key.search(currentPath)) === (-1)) {
console.log("No found !");
} else {
console.log("Found");
callback(jsonparse);
}
});
},
function(jsonparse){
console.log("In the 2nd loop !");
}
]);
can you try this
async.waterfall([
function readFile(callback){
console.log("Start async");
var params = {Bucket : "MyBucket", Key: "MyKey"};
reads3.getObject(params, function extract(err,data) {
//read a json object
console.log("Start reading");
callback(err,data);
});
},
function(data, callback){
var content = data.Body.toString('utf-8').trim();
var jsonparse = JSON.parse(content);
async.each(config, function(item) {
var currentPath = item.path;
if((key.search(currentPath)) === (-1)) {
console.log("No found !");
} else {
console.log("Found");
callback(null,jsonparse);
}
});
},
function(jsonparse,callback){
console.log("In the 2nd loop !");
callback(null,'result');
}
],function(err,data){
//your data is result
//......Your function script
});

Resources