Mobilpadde Mobilpadde - 3 months ago 16
Node.js Question

Make chained functions wait for each other to execute

How do I make a chained function wait for the function before it, to execute properly?

I have this excerpt from my module:

var ParentFunction = function(){
this.userAgent = "SomeAgent";
return this;
}

ParentFunction.prototype.login = function(){
var _this = this;

request.post(
url, {
"headers": {
"User-Agent": _this.userAgent
}
}, function(err, response, body){
return _this;
})
}

ParentFunction.prototype.user = function(username){
this.username = username;
return this;
}

ParentFunction.prototype.exec = function(callback){
request.post(
anotherURL, {
"headers": {
"User-Agent": _this.userAgent
}
}, function(err, response, body){
callback(body);
})
}

module.exports = parentFunction;


And this is from within my server:

var pF = require("./myModule.js"),
parentFunction = new pF();

parentFunction.login().user("Mobilpadde").exec(function(data){
res.json(data);
});


The problem is, that the
user
-function won't wait for
login
to finish (Meaning, it executes before the login returns
_this
). So how do I make it wait?

Answer

You can't make Javascript "wait" before executing the next call in the chain. The whole chain will execute immediately in sequential order without waiting for any async operations to complete.

The only way I can think of to make this structure work is to create a queue of things waiting to execute and then somehow monitor the things in that queue that are async so you know when to execute the next thing in the queue. This requires making each method follow some sort of standard convention for knowing both whether the method is async and if it is async when the async operation is done and what to do if there's an error in the chain.

jQuery does something like this for jQuery animations (it implements a queue for all chained animations and calls each animation in turn when the previous animation is done). But, its implementation is simpler than what you have here because it works only with jQuery animations (or manually queued functions that follow the proper convention), not with other jQuery methods. You are proposing a mix of three different kinds of methods, two of which are not even async.

So, to make a long story shorter, what you are asking for could likely be done if you make all methods follow a set of conventions, but it's not easy. Since you appear to only have one async operation here, I'm wondering if you could do something like this instead. You don't show what the .exec() method does, but if all it does is call some other function at the end of the chain, then you'ld only have one async method in the chain so you could just let it take a callback and do this:

parentFunction.user("Mobilepadde").login(function(data) {
    res.json(data);
});

I was working on a queued means of doing this, but it is not something I can write and test in less than an hour and you'd have to offer some ideas for what you want to do with errors that occur anywhere in the chain. Non-async errors could just throw an exception, but async errors or even non-async errors that occur after an async operation completes can't just throw because there's no good way to catch them. So, error handling becomes complex and you really shouldn't embark on a design path that doesn't anticipate appropriate error handling.

The more I think about this, the more I think you either want to get a library designed to handle the sequencing of async operations (such as the async module) and use that for your queueing of operations or you want to just give up on the chaining and use promises to support sequencing of your operations. As I was thinking about how to do error handling in the task queue with async operations, it occurred to me that all these problems have already been dealt with in promises (propagation of errors through reject and catching of exceptions in async handlers that are turned into promise rejections, etc...). Doing error handling right with async operations is difficult and is one huge reason to build off of promises for sequencing async operations.


So, now thinking about solving this using promises, what you could do is to make each async method return a promise (you can promisfy the entire request module with one call with many promise libraries such as Bluebird). Then, one .login() and .exec() return promises, you can do this:

var Promise = require('bluebird');
var request = Promise.promisifyAll(require('request'));

ParentFunction.prototype.login = function(){
    return request.postAsync(
        url, {
        "headers": {
            "User-Agent": this.userAgent
        }
    });
}

ParentFunction.prototype.exec = function(){
    return request.postAsync(
        anotherURL, {
        "headers": {
            "User-Agent": this.userAgent
        }
    }).spread(function(response, body) {
        return body;
    })
}

parentFunction.login().then(function() {
    parentFunction.user("Mobilpadde");
    return parentFunction.exec().then(function(data) {
        res.json(data);
    });
}).catch(function(err) {
    // handle errors here
});

This isn't chaining, but it gets you going in minutes rather than something that probably takes quite awhile to write (with robust error handling).