Todilo
Todilo

Reputation: 1333

Using callback function with prototype functions

I am having trouble figuring out how to pass the objects method rather than sort "generic prototype" method when doing callback.

function Client() {
    this.name = "hello";
}

Client.prototype.apiCall = function(method, params, callback) {
    callback();
}


Client.prototype.onLogin = function(error, data) {
    console.log(this.name);// undefined!!!!
}

Client.prototype.start = function() {
    var self = this;
    self.apiCall('rtm.start', {
    }, self.onLogin) // passing of method like this does not work.
}

I am passing the onLogin method but well it does not work. This is code I have re-written. Previously I nested all methods inside the Client function but well, I learned that that is not the way to do it so now I am trying using prototype.

I know there is some solution "binding" the onLogin function inside the Client() function but well I want to understand the issue.

Upvotes: 7

Views: 1728

Answers (2)

KooiInc
KooiInc

Reputation: 122906

You can use call or apply to bind this, see snippet. I've modified your code for demonstration purposes. Hope it clarifies things for you

function Client() {
  this.name = "hello";
}

Client.prototype = {
  apiCall: function(method, params, callback) {
    try {
      var trial = method.call(this, params);
      callback.apply(this, [null, trial]);
    } catch (e) {
      callback.apply(this, [e, null]);
    }
  },
  onLogin: function(error, data) {
    if (error) {
      Helpers.report('<b style="color: red">' +
        'An error occured!</b> <i>' +
        error.message + '</i>')
    } else {
      Helpers.report(this.name, ' (data.result = ' + data.result + ')');
    }
  },
  start: function() {
    Helpers.useCSS(1);
    
    // error from this.rtm.start
    Helpers.report('Command: <code>', 'this.apiCall(this.rtm.start, {will: \'not work\'}, this.onLogin);','</code>');
    this.apiCall(this.rtm.start, {will: 'not work'}, this.onLogin);
    // this.rtm.works is ok
    Helpers.report('<br>Command: <code>',
                   'this.apiCall(this.rtm.works, {will: \'work\'}, this.onLogin);',
                   '</code>');
    this.apiCall(this.rtm.works, {
      will: 'work'
    }, this.onLogin);
  },
  // --------------------------------
  // added rtm for snippet demo
  rtm: {
    start: function(params) {
      return anerror;
    },
    works: function(params) {
      return {
        result: 'worked, <code>params: ' + JSON.stringify(params) + '</code>'
      };
    }
  },
};

new Client().start(); //<= here
<script src="https://rawgit.com/KooiInc/Helpers/master/Helpers.js"></script>

Upvotes: 3

helpermethod
helpermethod

Reputation: 62155

You need to bind the apiCalls context to the callback using bind:

Client.prototype.apiCall = function(method, params, callback) {
    var bound = callback.bind(this);
    bound();
}

Otherwise, the this within onLogin is set to the global object.

See Call, Apply And Bind for further details.

Basically .bind(obj) returns a function which, when called, will internally use (obj) as this.
So you create this bound and then you call it.

Upvotes: 4

Related Questions