Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to solve 'this' problems with node libraries like async and request

I've written a node script that gets some data by requesting REST API data (using the library request). It consists of a couple of functions like so:

var data = { /* object to store all data */ },
function getKloutData() {
  request(url, function() { /* store data */}
}
// and a function for twitter data

Because I want to do some stuff after fetching all the I used the library async to run all the fetch functions like so:

async.parallel([ getTwitterData, getKloutData ], function() {
    console.log('done');
});

This all works fine, however I wanted to put everything inside a object pattern so I could fetch multiple accounts at the same time:

function Fetcher(name) { 
    this.userID = ''
    this.user = { /* data */ }
    this.init();
}
Fetcher.prototype.init = function() {
    async.parallel([ this.getTwitterData, this.getKloutData ], function() {
        console.log('done');
    });
}
Fetcher.prototype.getKloutData = function(callback) {
    request(url, function () { /* store data */ });
};

This doesn't work because async and request change the this context. The only way I could get around it is by binding everything I pass through async and request:

Fetcher.prototype.init = function() {
    async.parallel([ this.getTwitterData.bind(this), this.getKloutData.bind(this) ], function() {
        console.log('done');
    });
}
Fetcher.prototype.getKloutData = function(callback) {
    function saveData() {
        /* store data */
    }


    request(url, saveData.bind(this);
};

Am I doing something basic wrong or something? I think reverting to the script and forking it to child_processes creates to much overhead.

like image 256
askmike Avatar asked Jul 11 '12 08:07

askmike


2 Answers

You're doing it exactly right.

The alternative is to keep a reference to the object always in context instead of using bind, but that requires some gymnastics:

Fetcher.prototype.init = function() {
    var self = this;
    async.parallel([
        function(){ return self.getTwitterData() },
        function(){ return self.getKloutData() }
    ], function() {
        console.log('done');
    });
}

Fetcher.prototype.getKloutData = function(callback) {
    var self = this;

    function saveData() {
        // store data
        self.blah();
    }

    request(url, saveData);
};

You can also do the binding beforehand:

Fetcher.prototype.bindAll = function(){
    this.getKloutData = this.prototype.getKloutData.bind(this);
    this.getTwitterData = this.prototype.getTwitterData.bind(this);
};

Fetcher.prototype.init = function(){
    this.bindAll();
    async.parallel([ this.getTwitterData, this.getKloutData ], function() {
        console.log('done');
    });
};
like image 85
Ricardo Tomasi Avatar answered Oct 21 '22 21:10

Ricardo Tomasi


You can save this into another variable:

var me = this;

Then me is your this.

like image 20
Charles Avatar answered Oct 21 '22 22:10

Charles