app.get('/ratings', function (req, res){
db.database.find({rating:5}).count(function(err, doc) {
review.FiveStarCount=doc;
});
db.database.find({rating:4}).count(function(err, doc) {
review.FourStarCount=doc;
});
I am new to using the MEAN stack and trying to return the values of the number of 5 and 4 star reviews. I can retrieve the number of 5 stars just fine, but when I do multiple , I seem to be running into trouble and not sure how to set it up.
You kind of need to get the gist of how to handle control flow while doing async operations.
The function execution would not stop, unlike php, when you are running an async operation.
To accomplish what you are trying to do, the second db call needs to be inside the callback of the first. Try this out.
app.get('/ratings', function (req, res, next) {
db.database.find({rating:5}).count(function(err, doc) {
// always handle error first
if (err) {
return next(err);
}
if (doc) {
review.FiveStarCount = doc;
}
db.database.find({rating:4}).count(function(err, doc) {
if (err) {
return next(err);
}
if (doc) {
review.FourStarCount = doc;
}
console.log(review.FiveStarCount, review.FourStarCount);
});
});
});
So it seems like you are running against the async nature of JavaScript. in the case of the functions above, they will not execute in the order you have written (and you wouldn't want them too!) because they are async. There are few simple solutions to get you going while you learn more about the problem.
Solution 1: Nested callbacks. Hard to read. also, it waits for the first to complete before firing the second, possibly slowing everything down.
app.get('/ratings', function(req, res) {
var review = {};
db.database.find({rating:5}).count(function(err, doc) {
review.FiveStarCount = doc;
db.database.find({rating:4}).count(function(err, doc) {
review.FourStarCount = doc;
// DO something with data
res.json(review);
});
});
Solution 2: Latch. This has the advantage of making the requests in parallel and not being as awful to read.
app.get('/ratings', function(req, res) {
var review = {};
db.database.find({rating:5}).count(function(err, doc) {
review.FiveStarCount=doc;
if (review.FiveStarCount && review.FourStarCount) {
// DO something here
// Will only execute if the other has finished
}
});
db.database.find({rating:4}).count(function(err, doc) {
review.FourStarCount=doc;
if (review.FiveStarCount && review.FourStarCount) {
// DO something here
// Will only execute if the other has finished
}
});
});
There are course more solutions, such as maybe a better query or promises. Maybe read a bit more about async JavaScript here to get a better understanding.
Related
Code
Server.js result
My code is this
app.post('/get_page_data', function(req, res) {
function find (i,documents)
{
Item_data.find({item_name:documents[i].item_name}).exec(function (err,asd){
console.log(documents[i].item_name+": found");
console.log(asd[0].like);
}
)
}
Page_data.find().lean().exec(function (err, documents) {
var doc=documents;
async.series([
// 1st
function(done){
for(var i=0;i<documents.length;i++)
{
find(i,doc);
done()
}
},
// 2nd
function(done){
console.log("1");
console.log(doc);
console.log("2");
res.end(JSON.stringify(doc));
console.log("3");
done()
}
]);
}
)});
I'll introduce my code simply
when ajax called /get_page_data
i bring the pages data (page1,page2,page3 ...) in documents
every page has a item_name but pages data does not have the "like" data
so i find the value of "like" in the other collection(Item_data) by the same item name and put the "like" in the pages data documents
but this makes me crazy
the pages data is sent before i put the like value
so i read about the non blocking io and async blahblah...
and i found the async module. And used it.
But as you can see the pictures above, they're not working
(before trying async moudle they didn't work neither)
I don't know why res.end part excutes previously and Please tell me the solution of this situation Thank you for reading
Your find function is doing an asynchronous call, so the done() is being called before it returns any data.
You should move it inside the inner find callback.
Edit: Using async.each instead of async.series directly.
app.post('/get_page_data', function(req, res) {
function find (document, cb) {
Item_data.find({item_name:document.item_name}).exec(function (err,asd) {
console.log(document.item_name+": found");
console.log(asd[0].like);
cb();
})
}
Page_data.find().lean().exec(function (err, documents) {
var doc=documents;
async.each(documents,
// 1st
find(document, cb),
// 2nd
function() {
console.log("1");
console.log(doc);
console.log("2");
res.end(JSON.stringify(doc));
console.log("3");
});
}
)});
PS: there's also a lot of things that you should improve in that code. But that way it should work.
put the done thing inside the aysn function and call the done when the index value
app.post('/get_page_data', function(req, res) {
function find (i,documents,cb)
{
Item_data.find({item_name:documents[i].item_name}).exec(function (err,asd){
console.log(documents[i].item_name+": found");
console.log(asd[0].like);
console.log(i +"/"+ documents.length);
documents[i]["like"]=asd[0].like;
if(i==documents.length-1) cb();
//call done() when the index reached maximum
})
}
Page_data.find().lean().exec(function (err, documents) {
var doc=documents;
async.series([
// 1st
function(done){
for(var i=0;i<documents.length;i++)
{
find(i,doc,done); //put the done thing inside the function
}
},
// 2nd
function(done){
console.log("1");
console.log(doc);
console.log("2");
res.end(JSON.stringify(doc));
console.log("3");
done()
}
]);
working properly
I'm trying out the framework node.js on one of my projects.
I'm really seeing some good advantages on what they called "event-driven, non-blocking I/O model" however if my project there are some moments where I don't necessarily want to have some asynchronous calls and to be able to several operation before launching some asynchronous call.
Especially when I want to do some factorization and create some functions.
Typically I have the following case:
I know that in several part of my program I have to check if a media is existing in my database for a given string or id.
So as a guy who tried to stay organize I want to create a function that I will call each time I need to check this.
However, I did not find the way to do that with node.js and pg (the npm PostgreSQL library (https://github.com/brianc/node-postgres/) . Indeed, there is always a callback in the function and the return is null because of the callback. Here is an example below
/*
Function which is supposed to check if a media existing
*/
function is_media_existing (url_or_id){
log.debug("is_media_existing : begin of the function", {"Parameter" : url_or_id});
pg.connect(connectionstring, function (err, client, done) {
if (err) {
log.warning("is_media_existing : Problem with Database connection", {
"Parameter": url_or_id,
"Error": err
});
}
if (isNaN(url_or_id)) {
// Case is parameter is not a number (string)
var query = client.query('SELECT COUNT(*) as count FROM media WHERE url = $1::string ', url_or_id);
query.on('error', function (error) {
log.warning("is_media_existing : Problem with Database query (connection to db passed but not query " +
"", {"Parameter": url_or_id, "Error": error});
});
return query;
} else {
// Case is parameter is a int
log.debug("is_media_existing : Type of Parameter is a string");
// Case is parameter is not a number (string)
var query = client.query('SELECT COUNT(*) as count FROM media WHERE id = $1::id ', url_or_id);
query.on('error', function (error) {
log.warning("is_media_existing : Problem with Database query (connection to db passed but not query " +
"", {"Parameter": url_or_id, "Error": error});
});
return query;
}
});
}
// Executing the function
var test = is_media_existing("http://random_url_existing_in_db");
// test is always null as the return is in a callback and the callback is asynchronous
i have the feeling my question is touching the core concepts of node.js, and perhaps my approach is wrong and I apologize in advance.
I know it's not good to wait for a response before doing something.
But what's the alternative? How can I factorize my code into functions when I need some functionalities in several part of my code?
So if there would be anyone who could explain how to do that with a best practice of programming it would be great.
Thanks
Anselme
As Cody says, you probably dont want to do synchronous function.
The way you should handle the situation in your example is to pass in your own callback like this
function is_media_existing (url_or_id, callback){
and then instead of return query; use your callback like this-
callback(query);
or probably better to follow the node convention for callback functions to have two parameters (err, result) so your callback would look like this
callback(null, query);
Here is a rework of your sample
function is_media_existing (url_or_id, callback){ /* callback(err, result) */
log.debug("is_media_existing : begin of the function", {"Parameter" : url_or_id});
pg.connect(connectionstring, function (err, client, done) {
if (err) {
done(err);
log.warning("is_media_existing : Problem with Database connection", {
"Parameter": url_or_id,
"Error": err
});
return callback(err, null);
/* note that this return is simply used to exit the connect's callback and the return value is typically
* not used it is the call to callback() that returns the error value */
}
var qrystr;
if (isNaN(url_or_id)) {
log.debug("is_media_existing : Type of Parameter is a string");
qrystr = 'SELECT COUNT(*) as count FROM media WHERE url = $1::string;';
} else {
qrystr = 'SELECT COUNT(*) as count FROM media WHERE id = $1::id;';
}
client.query(qrystr, [url_or_id], function(err, result){
done();
if(err){
/* .. */
}
callback(err, result);
});
});
}
// Executing the function
var test = is_media_existing("http://random_url_existing_in_db", function(err, result){
if(err){
}else {
}
});
If you end up with a hard nest of callbacks, promises are really worth looking into.
I don't think you really do want a synchronous call. The problem with synchronous calls in node is that it stops the entire process from doing anything while a synchronous function is running as it will stop the event loop. As an example lets say your sync function takes 2 seconds to complete. Your server will then do nothing for 2 full seconds. That 2 seconds includes everything (accepting new connections, everything else, etc). The reason blocking functions don't exist is because they are (very) bad. Here is an example how your function will react in an async manor.
is_media_existing("http://random_url_existing_in_db", function(exists){
if (exists){
//do stuff
} else {
//do this other stuff
}
});
Then within is_media_existing you will need to call that callback function when your query completes.
//psuedo
function is_media_existing(url, callback){
query('select COUNT(*) as count FROM media WHERE id = $1::id '. [url], function(err, result){
if (err)
callback(false)
else
callback(result.count > 0)
})
}
With the new ES6 plus async stuff and babel its simpler. You can npm i -g babel npm i babel-runtime then compile and run the following with babel test.js --optional runtime --stage 2 | node. Please read the following example carefully to see how to adapt it to your use case:
let testData = [
{ id: 0, childIds: [1,2]},
{ id: 1, childIds:[] }
];
function dbGet(ids) {
return new Promise( r=> {
// this an example; you could do any db
// query here and call r with the results
r(ids.map((id) => { return testData[id];}));
});
}
async function dbExists(ids) {
let found = await dbGet(ids);
return (found && found.length>0);
}
async function test() {
var exists = await dbExists([0]);
console.log(exists);
}
test().then(f=>{}).catch( e=> {console.log('e',e)});
I'm building a twitter clone using Node.js and MongoDB with mongoose. My Tweet model has body, user and created fields where user is the id of the user who has created the tweet. Now I'm building the API. I want when I make a GET request to receive a list of all the tweets (/api/tweets/) but except the user field (which returns only the id of the user) I want to get the whole user object so that I can display information about the tweet owner in my front-end part. I ended up with the following code.
exports.all = function (req, res, next) {
Tweet.find({}, function (err, tweets) {
if (err) return res.json(400, err);
var response = [];
tweets.forEach(function (element, index, array) {
var tweet = {};
tweet._id = element._id;
tweet.created = element.created;
tweet.body = element.body;
User.findById(element.user, function (err, user) { // <- This line
if (err) return res.json(400, err);
tweet.user = user;
});
response.push(tweet);
});
return res.json(response);
});
};
It works perfectly except that it doesn't add the user info. The problem is in the line I have marked. When javascript comes to that line, it tries to make it "parallel" and continues with the code without executing the callback function. But then it pushes the tweet object that doesn't have yet user info. How can I fix this?
You're going to want to use the async library. It will make your life much easier.
// inside `Tweet.find`
async.each(tweets, function(done) {
// do stuff to tweets
User.findById(element.user, function(err, user){
if (err) return done(err);
// do stuff with user
done();
});
}, function(err) {
// called when done
res.json(response);
});
The issue is that res.json sends the response so it doesn't matter that findById is being called. You need to call res.json once everything is done.
You can do this in several ways, but the easiest one with your existing code is to just keep a counter:
var tweetCount = 0;
tweets.forEach(/* snip */) {
User.findById(element.user, function (err, user) {
tweet.user = user;
tweetCount++;
response.push(tweet);
if (tweetCount == tweets.length) {
res.json(response);
}
});
});
You can use Q promise library to sync. It is simple and easy to use.
Response will only be send when whole promise chain is compeleted
var result = Q();
tweets.forEach(function (tweet, i) {
result = result.then(function () {
// do your stuff
}
result.then(function () {
res.json(response); //successfully completed
});
I'm trying to use a callback to get the result of a query from a database, but I am still getting undefined returned to me. Here is the code:
function onComplete(x){
return x;
}
function getRecords(callBack){
mongo.MongoClient.connect("mongodb://localhost:27017/nettuts", function(err, db){
if(err){
console.log(err);
return;
}
console.log('Connected to mongo');
db.collection('nettuts').find().toArray(function(err, records){
if(typeof callBack === "function"){
callBack(records);
}
});
});
}
app.get('/', function (req, res) {
var records = getRecords(onComplete);
console.log(records);
res.render("index", {title: "Hello!", people: records});
});
On the third to last line, I am getting an undefined.
As stated, you are following this the wrong way around for asynchronous programming, if you want "re-use" then you are passing in the callback to the function you are re-using and not the other way around:
var mongo = require("mongodb"),
MongoClient = mongo.MongoClient;
function getRecords(db,callback) {
if (typeof callback !== 'function')
throw "getRecords() requires a callback as the second arg";
db.collection("test").find().toArray(function(err,records) {
callback(err,records);
});
}
MongoClient.connect('mongodb://localhost/test',function(err,db) {
getRecords(db,function(err,result) {
if (err) throw err;
console.log(result);
});
});
Also noting here that any of your active code needs to happen "after" the connection is present, too broad to cover here on the best way to do that. But you certainly do not establish a connection with every request, as that would be very bad.
You "re-usable" function then just accepts the connection details and the actual functionality you want to happen when the operation is complete. That is the basic principle of callbacks.
I'm not clear as to why return is used or not in the following code. Please explain when it should and shouldn't be used or required to return the response.
update: function(req, res) {
var id = req.param('id');
User
.update(id, req.params.all())
.exec(function(err, users) {
if(err) return res.json(err, 400);
var user = users[0];
console.log('ID', user.id);
User
.findOne(user.id)
.populate('profile')
.exec(function (err, user){
if (err) return res.json(err, 400);
res.json(user, 201);
});
});
},
return is used in the code in question as opposed to else. The code could be rewritten as:
update: function(req, res) {
var id = req.param('id');
User
.update(id, req.params.all())
.exec(function(err, users) {
if(err) {res.json(err, 400);}
// note the lack of "return" above, because in the case of an error
// the code below will never run
else {
var user = users[0];
console.log('ID', user.id);
User
.findOne(user.id)
.populate('profile')
.exec(function (err, user){
if (err) {res.json(err, 400);} // again, no return
else {res.json(user, 201);}
});
}
});
};
But that requires you to keep track of more braces, and is possibly less efficient (although v8 probably optimizes the else in these cases). You'll also see this convention used often when callbacks are involved:
function (req, res, next) {
if (!req.session.loggedInUser) {
return res.forbidden();
}
return next();
}
Again, you could rewrite using with else:
function (req, res, next) {
if (!req.session.loggedInUser) {
res.forbidden();
} else {
next();
}
}
Notice an important difference here: in the elseless code, if you leave out the first return you'll get into trouble because you'll send the res.forbidden response and execute the next() method, which you almost certainly don't want. On the other hand in the returnless code, if you were to add some more code after the if/else, it would get executed no matter what, which you also don't want. So a good convention is to always return when you're using a callback (like next()) or executing code that will send a full response to the client (like res.send or res.json).
return isn't used because there's a possibility that the code will be doing asynchronous operations. Async operations run "in parallel" or "later" or "just not in sequence", thus breaking the sequence of your code (Think of threads).
The usual practice is to provide a function (usually called a "callback") or an object that hosts a bunch of functions that can be called when your async operation finishes, instead of a return.