shudima shudima - 6 months ago 22
Node.js Question

Accessing 'this' of an object inside promise callback (then)

I want to create an object in Javascript.

One of the methods should execute a promise chain. Each of the methods in the chain have to access a config variable that is a member of the object.
The problem is, the

this
operator is changed in
PromiseMethod2
and I can't access the config variable (It works correctly in
PromiseMethod1
).

Here's my code:

var SomeObject(config) {
var that = this;
that.config = config;
}

SomeObject.prototype.SomeMethod = function() {
var that = this;

that.PromiseMethod1()
.then(that.PromiseMethod2)
.catch(console.error);
}

SomeObject.prototype.PromiseMethod1 = function() {
var that = this;
config = that.config;

return SomePromise();
}

SomeObject.prototype.PromiseMethod2 = function(someParams) {
var that = this;
config = that.config;
params = someParams;

return SomePromise();
}


var someObject = new SomeObject(someConfig);
someObject.SomeMethod().then(function () {
console.log('Done!');
}


I want to use the method delegate in the chain instead of just executing:

that.PromiseMethod1().then(function(response) { return that.PromiseMethod2(that, response); };


I can't use the
bind
method because it looks like it gets rebinded when the callback is executed.

Is there a solution to this?
Why's there a difference between
PromiseMethod1
and
PromiseMethod2
?

Answer

My real recommendation is not to use this or new at all (and you can use Object.create if you still want inheritance):

var SomeObject = function(config) {
    return {
        PromiseMethod1: function(){
            return somePromise(config.foo);
        },
        PromiseMethod2: function(x){
            return someOtherPromise(config.bar, x);
        }
    }
}

var instance = SomeObject({config: true});
instance.PromiseMethod1().then(instance.PromiseMethod2);

Here I'm using closures, and their ability to enclose variables of their parent lexical scope, to my advantage. Rather than relying on JavaScript to magically inject a this into my function at run-time based on which object the function is called on, because as demonstrated by your problem, that doesn't always work.

However, I know that its an unconventional way to work, so if you'd rather stick with this, you'll need to use bind in order to tell JavaScript which magical this-value the function belongs to:

var SomeObject function(config) {
    this.config = config;
}

SomeObject.prototype.PromiseMethod1 = function(){
    return somePromise(this.config.foo);
}

SomeObject.prototype.PromiseMethod1 = function(x){
    return someOtherPromise(this.config.bar, x);
}

var instance = new SomeObject({config: true});
instance.PromiseMethod1().then(instance.PromiseMethod2.bind(instance)); //<- :(

In your example SomeMethod you're not actually using bind. You still need to bind because you're passing the function into .then(f), and the code which receives the function doesn't know anymore which object it should use for the this. Now look at my earlier recommended code again. There are no thisses in there, so those functions don't rely at all on which object they're being called on, you can pass them around as higher-order-functions as much as you want without ever having to bind or that = this. :)