Avoiding callback hell in nodeJs / Passing variables to inner functions - javascript

Here's an example of something I'd like to simplify:
exports.generateUrl = function (req, res) {
var id = req.query.someParameter;
var query = MyMongooseModel.findOne({'id': id});
query.exec(function (err, mongooseModel) {
if(err) {
//deal with it
}
if (!mongooseModel) {
generateUrl(Id,
function (err, text, url) {
if (err) {
res.status(HttpStatus.INTERNAL_SERVER_ERROR).send(err);
return;
}
var newMongooseModel = new AnotherMongooseModel();
newMongooseModel.id = id;
newMongooseModel.save(function (err) {
if (err) {
res.status(HttpStatus.INTERNAL_SERVER_ERROR).send(err);
} else {
res.send({url: url, text: text});
}
});
});
} else {
//deal with already exists
}
});
};
I've seen other SO answer where they tell you to use named functions, but don't say how to deal with variable you want to pass in or use jQuery's queue. I do not have the luxury of either.
I understand that I can replace my anonymous functions with names functions, but then I would need to pass arounds variables. How would my inner function access res for instance if the function is defined elsewhere?

The core to your question is:
I understand that I can replace my anonymous functions with names functions, but then I would need to pass arounds variables. How would my inner function access res for instance if the function is defined elsewhere?
The answer is to use a function factory.
In general, this:
function x (a) {
do_something(function(){
process(a);
});
}
can be converted to this:
function x (a) {
do_something(y_maker(a)); // notice we're calling y_maker,
// not passing it in as callback
}
function y_maker (b) {
return function () {
process(b);
};
}
In the code above, y_maker is a function that generates a function (let's call that function's purpose "y"). In my own code, I use the naming convention .._maker or generate_.. to denote that I'm calling a function factory. But that's just me and the convention is in no way standard or widely adopted in the wild.
So for your code you can refactor it to:
exports.generateUrl = function (req, res) {
var id = req.query.someParameter;
var query = MyMongooseModel.findOne({'id': id});
query.exec(make_queryHandler(req,res));
};
function make_queryHandler (req, res) {
return function (err, mongooseModel) {
if(err) {
//deal with it
}
else if (!mongooseModel) {
generateUrl(Id,make_urlGeneratorHandler(req,res));
} else {
//deal with already exists
}
}}
function make_urlGeneratorHandler (req, res) {
return function (err, text, url) {
if (err) {
res.status(HttpStatus.INTERNAL_SERVER_ERROR).send(err);
return;
}
var newMongooseModel = new AnotherMongooseModel();
newMongooseModel.id = id;
newMongooseModel.save(make_modelSaveHandler(req,res));
}}
function make_modelSaveHandler (req, res) {
return function (err) {
if (err) res.status(HttpStatus.INTERNAL_SERVER_ERROR).send(err);
else res.send({url: url, text: text});
}}
This flattens out the nested callbacks. As an additional benefit, you get to properly name what the function is supposed to do. Which I consider good practice.
It also has the added advantage that it is significantly faster than when using anonymous callback (either with nesting callbacks or with promises, though if you pass named functions to promise.then() instead of anonymous functions then you'll get the same speed up benefits). A previous SO question (my google-fu is failing me today) found that named functions are more than twice the speed (if I remember correctly it was more than 5 times faster) of anonymous functions in node.js.

Use promises. Using Q and mongoose-q it would give: something like that:
exports.generateUrl = function (req, res) {
var id = req.query.someParameter;
var text = "";
var query = MyMongooseModel.findOne({'id': id});
query.execQ().then(function (mongooseModel) {
if (!mongooseModel) {
return generateUrl(Id)
}).then(function (text) {
var newMongooseModel = new AnotherMongooseModel();
newMongooseModel.id = id;
text = text;
newMongooseModel.saveQ()
}).then(function (url) {
res.send({url: url, text: text});
}).fail(function(err) {
res.status(HttpStatus.INTERNAL_SERVER_ERROR).send(err);
});
};

Named functions will be executed within the same scope that the anonymous functions are and would have access to all of variables you are currently using. This approach would make your code less nested and more readable (which is good) but would still technically be in "callback hell". The best way to avoid situations like this is to wrap your asynchronous libraries (assuming they don't already provide promises) with a promise library like Q. IMO, promises provide a much more clear picture of the execution path.
You can avoid the predicament of not knowing where variables came from by binding parameters to your named function using bind, for instance:
function handleRequest(res, err, text, url) {
if (err) {
res.status(HttpStatus.INTERNAL_SERVER_ERROR).send(err);
return;
}
var newMongooseModel = new AnotherMongooseModel();
newMongooseModel.id = id;
newMongooseModel.save(function (err) {
if (err) {
res.status(HttpStatus.INTERNAL_SERVER_ERROR).send(err);
} else {
res.send({url: url, text: text});
}
});
}
...
generateUrl(Id, handleRequest.bind(null, res));

Related

Passing values from within functions in Javascript

I have a javascript function "data.getOrdersByUsersModifiedDate" which is making a call to another function "database.getDb". The first function gets a "request" parameter object which has parameters defined. However I can't seem to be getting my head around as to how I can pass that parameter from data.getOrdersByUsersModifiedDate TO database.getDb so that I can use it at line
var orders = db.order.find().toArray(function(err, result)
How can I pass my request parameter from the top function "getOrdersByUsersModifiedDate" to lower function "database.getDb" so I can use it as a filter when I get orders
data.getOrdersByUsersModifiedDate = function (request, next) {
database.getDb(function(err, db) {
if(err) {
next(err);
} else {
var orders = db.order.find().toArray(function(err, result) {
if(err) return next(err);
next(null, result);
});
}
});
};
Use bind.
bind and apply allows a developer to specify the context of the function call (this).
Bind creates a new function with the arguments given to bind (from second parameter), whereas apply calls the function with the new context and the parameters passed as an array (see doc).
This will help you solve scoping hell in callback hell.
The poor man's way to achieve this is using let that=this.
Edit: As pointed by the OP in the comments, arrow functions in ES6 do not bind this ; thus using an arrow function solves the OP issue.
data.getOrdersByUsersModifiedDate = function (request, next) {
database.getDb(function(err, db) {
if(err) {
next(err);
} else {
let {arg1, arg2, arg3} = request; // This should work with ES6
var arg1 = request.arg1; // Otherwise this?
var orders = db.order.find().toArray(function(err, result) {
if(err) return next(err);
next(null, result);
});
}
}.bind(this)); // Scope this to the context of getOrdersByUsersModifiedDate
};

accessing method of the class inside of async module or

I just noticed if I have a method in my class I cannot access it with this keyword in my other prototypes if I call them in async.auto or... please see the sample code for more clarification and my workaround.
Would you do the same? In other words is this the most elegant way in Node.JS?
function foo(config) {
var self = Object.create(foo.prototype)
self.db = nano.db.use(config.db.dbName);
return self
}
foo.prototype.method1 = function () {
// The workaround to use this.db is to store this.db in a variable, is this elegant?!? Would you do the same?
var db = this.db;
async.auto({
check_DB: function (next) {
// do some operations here
next();
},
insert_DB: ['check_DB', function (callback, results) {
// Note1: interestingly this.db is not going to work! in other words here this.db is undefined
db.insert(value, function (err, body) {
//Do some other operations here
})
}]
});
}
foo.prototype.method2 = function () {
// The workaround to use this.db is to store this.db in a variable?!? Would you do the same?
var db = this.db;
db.get("baz", function (err, body) {
// Do some operatiuons
// Note2: interestingly this.db is not going to work here either!
db.get("bar", function (err, response) {
// do some other operations
})
}
});
}
It's exactly right. The async.auto(... function is going to change the scope of "this". It's not particular to async, but to calling a javascript function within another one.
Same deal with db.get(... in method2. It also changes the scope of "this" in the exact same way.
So it is quite normal in javascript code before calling a function where you want to access "this" from the outer scope to assign "this" to some other variable just, just as you've done with:
var db = this.db;
A lot of folks will assign this to something like: "self', "_this", "that", etc.

Why is this named anonymous js function working before it is defined?

I have a simple Gulpfile with a task defined. There is a named anonymous function that is defined after the Gulp task. The task uses this function and is working when I would expect to get undefined is not a function, but I am not. Here is the code:
gulp.task('bower', function() {
var bowerDir = 'bower_components';
fs.readdir(bowerDir, function(err, dirs) {
_.each(dirs, function(dir) {
var directory = dir;
fs.stat(path.join(bowerDir, dir), function(err, stats) {
if(stats.isDirectory()) {
listing('bower_components');
}
});
});
});
});
var listing = function(dir) {
console.log(dir);
};
Please explain why this is working?
gulp.task(), fs.readdir() and fs.stat() are all asynchronous functions. They call their callback functions sometime LATER, not immediately. That means that the code after that defines listing gets a chance to run BEFORE the callback is actually called. So, thus listing is defined before it is actually used.
I wouldn't suggest this as a good coding method myself because you are relying on the timing on things.
If, instead you defined your listing function like this:
function listing(dir) {
console.log(dir);
}
Then, you would not have a dependency on timing because all statically defined functions like this are parsed first and hoisted to the top of the scope they are defined in and thus always available in that scope ragardless of timing.
FYI, if you really want to show this to yourself, you can add this logging to see the actual timing and sequence of things:
function logTime(msg) {
console.log((new Date()).getTime() + ": " + msg);
}
logTime("start");
gulp.task('bower', function() {
var bowerDir = 'bower_components';
fs.readdir(bowerDir, function(err, dirs) {
_.each(dirs, function(dir) {
var directory = dir;
fs.stat(path.join(bowerDir, dir), function(err, stats) {
if(stats.isDirectory()) {
logTime("about to call listing()");
listing('bower_components');
}
});
});
});
});
logTime("about to define listing");
var listing = function(dir) {
logTime("listing() called");
console.log(dir);
};
Because that annonymous function is a callback function and most likely is called after the initialization of listing function.

Passing a variable to an asynchronous function (promise) in javascript

I have the following code which runs in a loop:
var index = fileNames[x].lastIndexOf("/") + 1;
var currentImageName = fileNames[x].substr(index);
if (currentImageName.indexOf(".jpg") != -1) {
reader.getFileAsBlob(fileNames[x])
.done(function(blob) {
picturesFilePathArray.push({
fileName: currentImageName,
fileURL: blobURL(blob)
});
refreshKMZList();
});
}
The problem I am having is that I am trying to save an object with 2 properties into an array. This object should have the identifier and the result. (fileName and fileURL respectively). But since this function is asynchronous (executed through a promise). By the time the "getFileAsBlob" finishes, currentImageName has already been updated ultimately ending in many of my objects having the same identifier (the last one processed before it finished).
This might be a really easy problem, but I am very new to javascript and haven't yet found anything about it.
I thought that the solution might be in passing the variable to the "done" function, but I think this function is the one being returned by the method and is already set. (I dont know how it looks)
Edit:
The code is just inside a normal loop
for (x = 0; x<fileNames.length; x++)
So create a function so the variable can not be changed
function getFile (filmName, imageName) {
reader.getFileAsBlob(fileName)
.done(function(blob) {
picturesFilePathArray.push({
fileName: imageName,
fileURL: blobURL(blob)
});
refreshKMZList();
});
}
and call it
if (currentImageName.indexOf(".jpg") != -1) {
getFile (fileNames[x], currentImageName);
}
or you can do something like
if (currentImageName.indexOf(".jpg") != -1) {
(function (fileName, imageName) {
reader.getFileAsBlob(fileName)
.done(function(blob) {
picturesFilePathArray.push({
fileName: imageName,
fileURL: blobURL(blob)
});
refreshKMZList();
});
})(fileNames[x], currentImageName);
}
MDN Closure
The solution to this problem is always the same: Use a closure.
But since you are using a promise based library, you have a nicer option. Use promises. (Internally this is based on closures as well, of course. It's just a much nicer abstraction.)
function getFileInfo(path) {
return reader.getFileAsBlob(path).done(function (blob) {
return {
fileName: path.split('/').pop(),
fileURL: blobURL(blob)
});
};
}
function isJpg(filename) {
return /\.jpg$/i.test(filename);
}
Now you can do this, where refreshKMZList() is called once per file:
fileNames.filter(isJpg).forEach(function (path) {
getFileInfo(path).then(function (fileInfo) {
picturesFilePathArray.push(fileInfo);
refreshKMZList();
})
.catch(function (error) {
// handle the error
});
});
or even this, where refreshKMZList() is called only once per overall:
var fileInfos = fileNames.filter(isJpg).map(getFileInfo);
Promise.all(fileInfos).then(function (arrayOfFileInfo) {
picturesFilePathArray.concat(arrayOfFileInfo);
refreshKMZList();
})
.catch(function (error) {
// handle the error
});
Read up on promises, they are worth being understood.

Javascript - functions on functions?

I've recently used a nice library for node.js called Kue.
I wanted to get some better understanding of what's going so I started reading the code...
I stumbled on to a piece of code and my mind went "WTF!!?!#$#!$"...
This is the code:
function get(obj) {
var pending = 0
, res = {}
, callback
, done;
return function _(arg){
switch (typeof arg) {
case 'function':
callback = arg;
break;
case 'string':
++pending;
obj[arg](function(err, val){
if (done) return;
if (err) return done = true, callback(err);
res[arg] = val;
--pending || callback(null, res);
});
break;
}
return _;
};
}
which being used like this:
exports.stats = function(req, res){
get(queue)
('inactiveCount')
('completeCount')
('activeCount')
('failedCount')
('delayedCount')
('workTime')
(function(err, obj){
if (err) return res.send({ error: err.message });
res.send(obj);
});
};
.
.
.
Are those functions on functions?!
How are they aware of each other?
What is that '_'(underscore) on the 7th row of the function?
Could someone please help me understad what's goin' on over there? :)
Functions can indeed return functions. Take this function, for example:
function func(text) {
alert(text);
return func;
}
Obviously the return value of any invocation of func will be, again, func, so you can use it like this:
func("hello")("world");
…and you'll get two alerts: first "hello", and then "world".
Next, there's something called a named function expression. You might have seen anonymous function expressions before:
doSomething(thing, function(err) {
// operation completed or something
});
That, of course, is great for simple things, but sometimes you want the function to have a name so it can refer to itself. As Kolink mentioned, if you just want to recurse, there's arguments.callee, which refers to the function currently executing, but there is another way: you can give the function a name visible only within the function while still having it be a function expression:
doSomething(thing, function myself(err) {
// ^^^^^^
// now I can refer to myself as myself!
});
An underscore is a valid identifier, so they're basically just combining these techniques in a way that may be difficult to understand.

Categories

Resources