Not long ago I started a small project, something like a todo list to start testing what I have learned here. Everything was going ok until I reached a page that needed values from several places (it’s a dashboard page). Soon I noticed how the ‘returns’ don’t work inside these callback functions. So my question is: How do you get values from methods that have a callback function? I know you can pass the value as a parameter in the callback and set it in the callback’s body, but i wonder if there is a better solution to this.
Example:
response.render('mypage', {
firstValue: functionToGetFirstValue(),
secondValue: functionToGetSecondValue(),
thirdValue: functionToGetSecondValue(),
... and so on
});
If I do the ‘pass the value as the callback parameter’ thing the code might get too messy. Then I would also have to validate if all the keys have something at the end of each of those methods before calling render (like that challenge where we had to ‘juggle’ various async calls)
The only way I see to get the values from those functions is as a side effect - that is, create a variable in the enclosing scope and set the value in the function. I’m completely unfamiliar with the pattern you’re describing, though. Is this render function a part of some library that requires an object full of callbacks, or you passing this as locals in Express?
function doSomething(callback){
amodule.doSomethingElseThatGetsAValue(function(thatValue){
callback(thatValue);
});
}
var value;
doSomething(function(thatValue){
value = thatValue;
};
instead of
var value = doSomething();
//and doSomething() has a return somewhere. doesn't work here
The render method is from app.get(’/aroute’, function( request, response){}). response.render’s first parameter is the html/jade/etc and the second one is a key/value object. I’m trying to fill up those values.
What (I think) you’re describing has a very good chance of behaving unpredictably and we should definitely find a different way to do this. Is there a way you could share the code you have so far? Talking about this in the abstract will be difficult, and if we can get to your particular use case we can probably reach a solution much more easily.
By the by, I edited your last post to make the code more readable. Instead of <code> tags, use three backticks (to the left of the 1 key). See this post for details.
Thanks for the tip I saw your edit and made the same change to the first post.
The code is in another machine but that section is really short so I’ll type it in here.
var mongo = require('mongodb').MongoClient;
var url = 'mongodb://host:port/database';
function taskCount(){
mongo.connect(url, function(err, db){
if(err) return 0; //this doesn't work
db.collection('tasks').count(function(err, count){
db.close();
return err ? 0 : count; //this doesn't work either
});
//returns placed outside work
}
module.exports = {
dashboard: function(request, response){
//use response.send or response.json instead to test this piece of code
response.render('dashboard', {
title: 'Dashboard', //the title set on head, there is a value passed through handlebars
taskCount: taskCount() //doesn't work because of the callback, returns undefined
});
}
};
My first inclination - and this may not work for you - is why not just render all of the tasks, and let the view display the length of the tasks array? You wouldn’t have to use the tasks necessarily, but I imagine you’d want to at some point, so why not handle both values with one database call? But here’s how I would refactor the code you have here to get the effect you want:
var mongo = require('mongodb').MongoClient;
var url = 'mongodb://host:port/database';
module.exports = function(request, response){
mongo.connect(url, function(err, db){
if(err) response.render('errorPage', {error: err}) //make sure the user knows there's an error
db.collection('tasks').count(function(err, count){
response.render('dashboard', {
title: 'Dashboard',
taskCount: count
});
db.close();
});
}
That’s a rough idea of what I would start with. I’ve always found vanilla Mongo to end up pretty janky, but it could also be that I just plain suck at it.
Yup, I’ve been doing this for pages that require just one type of data. The problem is when I want to add more keys inside that object it can get very messy:
Promises are absolutely the way to go here. I think Mongoose makes this sort of thing much easier, too. I much prefer being able to define a schema for data and let the `pooter box handle the object creation.
Yea I feel like maybe those promises can solve it. I’ll take a look at that mongoose module too. The amount of modules that can do the same thing is overwhelming so I chose the vanilla one. Thanks for the help
It’s just occurred to me that the aggregate pipeline would help here. Instead of multiple queries, you define one that takes care of everything, and then you could run the render method in the callback. This should work for every query you need because, as I understand it, all of the methods we use to get data in MongoDB are shorthand for predefined aggregate queries (docs). I’ve been in Rails land so long that my Mongo game is seriously weak.