Node js array dashboard - javascript

I'm doing the backend of my app with node js. In this case i'm trying to get the typical dashboard like facebook, instagram,....
Where for one user i'm trying to get the users that he follows. And when i get the array of users following, i find the "recetas" that they have (one user can have more than one). And finally i add all this recetas in an array but the problem is that is returning me empty.
getDashboard = function (req, res) {
var myarray = new Array();
//myarray = [];
User.findById(req.params.id, function (err, user) {
if (!user) {
res.send(404, 'User not found');
}
else {
var a = user.following;
a.forEach(function (current_value) {
Receta.find({ "user_id": current_value._id }, function (err, recetas) {
if (!err) {
recetas.forEach(function (receta) {
myarray.push(receta);
}
} else {
console.log('Error: ' + err);
}
});
})
res.send(myarray);
}
});
};

You are dealing with a common async issue. Receta.find is asynchronous, it is not a blocking operation, so res.send is called before all of your Receta.find calls have completed. You can get around this issue by using Promises, assuming they are available in your version of Node:
var a = user.following;
var promises = a.map(function(current_value) {
return new Promise(function(resolve, reject) {
Receta.find({"user_id":current_value._id}, function (err, recetas) {
if(!err) {
resolve(recetas);
} else {
reject(err);
}
});
});
});
Promise.all(promises).then(function(allData) {
res.send(allData);
}).catch(function(error) {
res.send(error);
});
If native Promises aren't available, you can use a library like Q or bluebird

res.send(myarray); is being called before a.forEach completes due to Receta.find which is I/O.
call res.send only when the loop is finished and recetas returned.

Related

Export an array containing mysql results to another javascript file

I making use of node.js + mysql in my app. I would like to export an array containing results/rows of an SQL select statement to another javascript file and display the values stored in the array.
stats.js
var phonenum='0718900000'
var sql='SELECT name,surname,age from Tbluser WHERE number=?;'
conn.query(sql,[phonenum],function(err,results){
if (err){
throw err;
};
/*if (results==0)
{
let dealersMessage="❌ User not found"
}*/
console.log(results)
var datastatagentStore=[];
results.forEach(item =>{
module.exports=datastatagentStore.push({
name:item.name,
surname:item.surname,
age:item.age,
})
})
app.js
var{datastatagentStore}=require('../functions/stats.js')
console.log('User details '+datastatagentStore);
Based on a comment above:
My goal is to execute the query and display the results to the user every time the user selects the 'view my details' button.
Then you don't want to export the results, you want to export the function which performs the query. That function would return [a Promise which resolves to] the results.
If conn.query returns a Promise then it might look something like this:
var getUserDetails = function (phonenum) {
var sql='SELECT name,surname,age from Tbluser WHERE number=?;'
return conn.query(sql, [phonenum], function(err, results) {
if (err) {
throw err;
};
console.log(results)
var datastatagentStore = [];
results.forEach(item => {
datastatagentStore.push({
name:item.name,
surname:item.surname,
age:item.age,
})
});
return datastatagentStore;
});
};
module.exports = getUserDetails;
Though I don't know if conn.query does return a Promise. If it doesn't, you can manually create one:
var getUserDetails = function (phonenum) {
return new Promise(function (resolve, reject) {
var sql='SELECT name,surname,age from Tbluser WHERE number=?;'
conn.query(sql, [phonenum], function(err, results) {
if (err) {
reject(err);
};
console.log(results)
var datastatagentStore = [];
results.forEach(item => {
datastatagentStore.push({
name:item.name,
surname:item.surname,
age:item.age,
})
});
resolve(datastatagentStore);
});
});
};
module.exports = getUserDetails;
Note the use of resolve and reject here to manually complete the Promise. But however you structure it, the overall point is that your module export wouldn't be the results of having executed the query, it would be the function to execute the query. And since executing the query is an asynchronous operation, that function should return a Promise.
Then you can use it like any other asynchronous operation:
var datastatagentStore = require('../functions/stats.js');
datastatagentStore('0718900000').then(function (details) {
console.log('User details ', details);
});

Adding bluebird promise to NodeJS module, then function is not defined

I'm new to promise and bluebird, in my current project, I have to deal with a lot async API calls, therefore I want to use JS promise as my main tool.
One of my imported module looks like this:
var Promise = require("bluebird");
var watson = Promise.promisifyAll(require('watson-developer-cloud'));
var conversation = watson.init();
exports.enterMessage = function (inputText) {
var result; //want to return
conversation.message({
input: {
"text": inputText
}
}, function(err, response) {
if (err) {
console.log('error:', err);
result = err;
}
else {
result = response.output.text;
}
});
console.log(result); //sync call, result === undefined
return result;
}
My question is how should I approach this question? I understand the example about using promise with IO such like fs. So I try to mimic the way by doingconversation.message(...).then(function(response){result = response.output.text}), but it says conversation.message(...).then() is not defined.
Thanks to jfriend00's link, I fixed my logic and used the correct way to handle this async call.
Here is the fixed code:
//app.js
var Promise = require("bluebird");
var conversation = Promise.promisifyAll(require('./watson-conversation'));
conversation.enterMessage(currentInput).then(function(val){
res.send(val)}
).catch(function(err){
console.log(err)
});
});
//watson-conversation.js
var conversation = watson.init();
exports.enterMessage = function (inputText) {
return new Promise(function(resolve, reject){
conversation.message({
input: {
"text": inputText
}
}, function(err, response) {
if (err) {
console.log('error:', err);
reject(err);
}
else {
resolve(response.output.text);
}
});
});
}

How to properly chain Promises with nesting

My node project currently contains a sideway christmas tree of nested callbacks in order to fetch data and process them in the right order. Now I'm trying refactor that using Promises, but I'm unsure how to do it properly.
Let's say I'm fetching a list of offices, then for each office all their employees and then each employees' salary. In the end all entities (offices, employees and salaries) should be linked together and stored in a database.
Some pseudo-code illustrating my current code (error handling omitted):
fetch(officesEndpoint, function (data, response) {
parse(data, function (err, offices) {
offices.forEach(function (office) {
save(office);
fetch(employeesEndPoint, function (data, response) {
parse(data, function (err, employees) {
// link each employee to office
save(office);
save(employee);
employees.forEach(function () {
fetch(salaryEndpoint, function (data, response) {
parse(data, function (err, salaries) {
// link salary to employee
save(employee);
});
});
});
});
});
});
});
});
I tried solving this with promises, but I have a couple of problems:
kind of verbose?
each office needs to be linked to their respective employees, but in the saveEmployees function I only have access to the employees, not the office from further up in the chain:
var restClient = require('node-rest-client');
var client = new restClient.Client();
var xml2js = require('xml2js');
// some imaginary endpoints
var officesEndpoint = 'http://api/offices';
var employeesEndpoint = 'http://api/offices/employees';
var salaryEndpoint = 'http://api/employees/:id/salary';
function fetch (url) {
return new Promise(function (resolve, reject) {
client.get(url, function (data, response) {
if (response.statusCode !== 200) {
reject(statusCode);
}
resolve(data);
});
});
}
function parse (data) {
return new Promise(function (resolve, reject) {
xml2js.parseString(data, function (err, result) {
if (err) {
reject(err);
}
resolve(result);
});
});
}
function saveOffices (offices) {
var saveOffice = function (office) {
return new Promise(function (resolve, reject) {
setTimeout(function () { // simulating async save()
console.log('saved office in mongodb');
resolve(office);
}, 500);
})
}
return Promise.all(offices.map(saveOffice));
}
function saveEmployees (employees) {
var saveEmployee = function (employee) {
return new Promise(function (resolve, reject) {
setTimeout(function () { // simulating async save()
console.log('saved employee in mongodb');
resolve(office);
}, 500);
})
}
return Promise.all(offices.map(saveEmployee));
}
fetch(officesEndpoint)
.then(parse)
.then(saveOffices)
.then(function (savedOffices) {
console.log('all offices saved!', savedOffices);
return savedOffices;
})
.then(function (savedOffices) {
fetch(employeesEndPoint)
.then(parse)
.then(saveEmployees)
.then(function (savedEmployees) {
// repeat the chain for fetching salaries?
})
})
.catch(function (error) {
console.log('something went wrong:', error);
});
You don't necesseraly have to nest, this would work too:
fetch(officesEndpoint)
.then(parse)
.then(saveOffices)
.then(function(savedOffices) {
console.log('all offices saved!', savedOffices);
return savedOffices;
})
.then(function(savedOffices) {
// return a promise
return fetch(employeesEndPoint); // the returned promise can be more complex, like a Promise.all of fetchEmployeesOfThisOffice(officeId)
})
// so you can chain at this level
.then(parse)
.then(saveEmployees)
.then(function(savedEmployees) {
return fetch(salariesEndPoint);
})
.catch(function(error) {
console.log('something went wrong:', error);
});
Your promisified functions fetch, parse, saveOffices and saveEmployees are fine. With those, you can refactor your current code to use promises, chain instead of nest where applicable, and leave out a bunch of error handling boilerplate:
fetch(officesEndpoint)
.then(parse)
.then(function(offices) {
return Promise.all(offices.map(function(office) {
return save(office)
.then(function(){ return fetch(employeesEndPoint); })
.then(parse)
.then(function(employees) {
// link each employee to office
// throw in a Promise.all([save(office), save(employee)]) if needed here
return Promise.all(employees.map(function(employee) {
return fetch(salaryEndpoint)
.then(parse)
.then(function(salaries) {
return Promise.all(salaries.map(function(salary) {
// link salary to employee
return save(employee);
}));
});
}));
});
}));
});
In the innermost loop callback, you've got all of office, employee and salary available to interlink them to your liking. You cannot really avoid this kind of nesting.
You'll get back a promise for a huge array of arrays of arrays of save results, or for any error in the whole process.
It is good approach to change this
if (response.statusCode !== 200) {
reject(statusCode);
}
resolve(data);
to this
if (response.statusCode !== 200) {
return reject(statusCode);
}
resolve(data);
In your example, the result will be same, but if you are making more things (like doing something in database) the unexpected result may occure, because without return the whole method will be executed.
This example
var prom = new Promise((resolve,reject) => {
reject(new Error('error'));
console.log('What? It did not end');
resolve('Ok, promise will not be called twice');
});
prom.then(val => {
console.log(val);
}).catch(err => {
console.log(err.message);
});
is having this output
What? It did not end
error
To the question - if you need access to more than one returned value (i.e. offices AND employies), you have basically two options :
Nested promises - this is not generally bad, if it "makes sense". Altought promises are great to avoid huge callback nesting, it is ok to nest the promises, if the logic needs it.
Having "global" variables - you can define variable in the scope of the promise itself and save results to it, therefore the promises are using these variables as "global" (in their scope).

Identify Closure Memory Leak

I'm currently writing a simple api where you post an array (length = 200) and since each element in the array needs to do 1-2 look up requests, I'm using the async library to control the flow of things. I'm using node 0.12.5 & Express.
router.post('/data', function(req, res, next) {
var cloudDB = db.cloudant.use('events');
var tempStorage = {"docs": []};
// This each loop is to make sure all events get iterated through before submitting response
async.each(req.body, function(singleEvent, loopCallback) {
// this should be async waterfall or something better to organize it
async.waterfall(
[
function(callback) { // get user data from db
db.getUserInfo(singleEvent.email, function (error, dbResponse) {
if(error) { // Houston, we have a problem
return callback(error);
}
return callback(null, dbResponse);
})
},
function(dbResponse, callback) { // decide what to do about results
if(!dbResponse) { // we were unable to get the user from DB
db.searchForUser(singleEvent.email, function (err, searchResponse) {
if(err)
return callback(err);
else
return callback(null, JSON.parse(searchResponse));
})
}
else {
return callback(null,JSON.parse(dbResponse));
}
},
function(userInfo, callback) { // combine data into proper logic
callback(null,combineEventAndUserData(singleEvent,userInfo));
}
],
function (err, result) {
// User event has been processed, so if there are no errors, lets add it to the queue
if(err) {
console.log(err);
}
else {
tempStorage.docs.push(result);
}
loopCallback(); // We're done with this singleEvent
}
)
}, function(err) { // function gets called when all singleEvents have been looped through
console.log("Finished each");
if(err) {
res.status(500).send(err);
}
else {
cloudDB.bulk(tempStorage, function(err, body) {
if(!err) {
res.status(200).send(body);
}
else {
res.status(500).send(err);
}
})
}
});
});
So, this code works! However... (sniff sniff), I seem to have created a memory leak. I have taken a look at both memwatch-next and heapdump, and all I've been able to tell was that 'arrays' keep growing when I look at the heap dump.
I don't know why, but I have a suspicion that this might have something to do with closures and how I'm storing the items generated from each of the waterfalls and perhaps the tempStorage.docs is not being released? Am I storing the tempStorage in the correct way? Or should I change how I do that?

Using Promise.all() for multiple http/oauth queries

I'm trying to wait for the output of two OAuth calls to an API, and I'm having trouble retrieving the data from those calls. If I use Promise.all(call1,call2).then() I am getting information about the request object.
First, here's the setup for the fitbit_oauth object:
var fitbit_oauth = new OAuth.OAuth(
'https://api.fitbit.com/oauth/request_token',
'https://api.fitbit.com/oauth/access_token',
config.fitbitClientKey,
config.fitbitClientSecret,
'1.0',
null,
'HMAC-SHA1'
);
foodpath = 'https://api.fitbit.com/1/user/-/foods/log/date/' + moment().utc().add('ms', user.timezoneOffset).format('YYYY-MM-DD') + '.json';
activitypath = 'https://api.fitbit.com/1/user/-/activities/date/' + moment().utc().add('ms', user.timezoneOffset).format('YYYY-MM-DD') + '.json';
Promise.all([fitbit_oauth.get(foodpath, user.accessToken, user.accessSecret),
fitbit_oauth.get(activitypath, user.accessToken,
user.accessSecret)])
.then(function(arrayOfResults) {
console.log(arrayOfResults);
}
I want arrayOfResults to give me the data from the calls, not information about the requests. What am I doing wrong here? I'm new to promises so I'm sure this is easy for someone who isn't.
The callback for a single fitbit_oauth call is as follows:
fitbit_oauth.get(
'https://api.fitbit.com/1/user/-/activities/date/' + moment().utc().add('ms', user.timezoneOffset).format('YYYY-MM-DD') + '.json',
user.accessToken,
user.accessSecret,
function (err, data, res) {
if (err) {
console.error("Error fetching activity data. ", err);
callback(err);
return;
}
data = JSON.parse(data);
console.log("Fitbit Get Activities", data);
// Update (and return) the user
User.findOneAndUpdate(
{
encodedId: user.encodedId
},
{
stepsToday: data.summary.steps,
stepsGoal: data.goals.steps
},
null,
function(err, user) {
if (err) {
console.error("Error updating user activity.", err);
}
callback(err, user);
}
);
}
);
Thanks to jfriend00 I got this working, here's the new code:
function fitbit_oauth_getP(path, accessToken, accessSecret) {
return new Promise (function(resolve, reject) {
fitbit_oauth.get(path, accessToken, accessSecret, function(err, data, res) {
if (err) {
reject(err);
} else {
resolve(data);
}
}
)
})};
Promise.all([fitbit_oauth_getP(foodpath, user.accessToken, user.accessSecret),
fitbit_oauth_getP(activitypath, user.accessToken, user.accessSecret)])
.then(function(arrayOfResults) {
console.log(arrayOfResults);
});
Promise.all() only works properly with asynchronous functions when those functions return a promise and when the result of that async operation becomes the resolved (or rejected) value of the promise.
There is no magic in Promise.all() that could somehow know when the fitbit functions are done if they don't return a promise.
You can still use Promise.all(), but you need to "promisify" the fitbit functions which is a small wrapper around them that turns their normal callback approach into returning a promise that was then resolved or rejected based on the callback result.
Some references on creating a promisified wrapper:
Wrapping Callback Funtions
How to promisify?
If you have an async function that accepts a callback to provide the async result such as fs.rename(oldPath, newPath, callback), then you can "promisify" it like this:
function renameP(oldPath, newPath) {
return new Promise(function(resolve, reject) {
fs.rename(oldPath, newPath, function(err) {
if (err) {
reject(err);
} else {
resolve();
}
});
});
};
renameP("orig.txt", "backup.txt").then(function() {
// successful here
}, function(err) {
// error here
});
Some promise libraries such as Bluebird have a built in .promisify() method that will do this for you (it will return a function stub that can be called on any function that follows the node.js async calling convention).

Categories

Resources