1

Using Node.js's Q library, when I try to pass an object prototype function to a .then resolver in a promise, it's losing context of this:

Foo.prototype.outsideResolve = function() {
    var that = this;
    return q.Promise(function(resolve, reject) {
        console.log(that); // {data: "foo"}
        resolve();
    });
};

Foo.prototype.insideResolve = function() {
    var that = this;
    return q.Promise(function(resolve, reject) {
        console.log(that); // undefined
        resolve();
    });
};

Foo.prototype.async = function() {
    var foo = this;

    return q.Promise(function(resolve, reject) {
        foo.outsideResolve()
            .then(foo.insideResolve)
            .then(resolve)
            .fail(reject)
            .done();
    });
};

Why does this happen? Is this expected behavior? Are you not supposed to be able to call prototype functions in promise resolves? Is there a way around this?

A. Duff
  • 4,097
  • 7
  • 38
  • 69

1 Answers1

2

this is not a static thing. It can change based on what the function using this is being invoked on. In some cases, this can be the object you've just invoked new on, but in this instance, it's not.

When you reach foo.insideResolve from the promise chain, the ThisBinding is lost. In order to fix this you can use the Function.prototype.bind method, which takes a varadic number of arguments - the first argument is the this context you want to bind the function to use, and the others are arguments that are partially applied. The bind function returns a function which when invoked passes the this and arguments to the original function.

In short:

use foo.insideResolve.bind(foo) instead of foo.insideResolve.

Dan
  • 10,282
  • 2
  • 37
  • 64
  • Thanks to you and elclanrs. Using `.bind` does do the trick. But I am still confused on why the `this` context is lost inside the promise resolve. If we have a reference to `foo` and we're clearly calling `foo`'s `insideResolve` function, shouldn't `this` still evaluate to that object? – A. Duff Jul 08 '15 at 17:40
  • it is a bit difficult to explain but essentially, no. I can't explain it very well but there is an [excellent blog post](https://javascriptweblog.wordpress.com/2010/08/30/understanding-javascripts-this/) that might help. The long and short of it though is that you basically don't want to rely on `this` being a certain value (this is a reason why some people prefer to avoid `this`). – Dan Jul 08 '15 at 17:42
  • 2
    The thing is that you *don't* keep a reference to foo when you write `foo.insideResolve` (without parentheses to call the function right there). You merely get a reference to the `insideResolve` function itself, and the fact that you found that reference via a property of `foo` is lost. Inside a function, `this` is determined when the function is *called*, not when you previously obtained a reference to the function. The promise calls `insideResolve` without any knowledge that it was a property of `foo`; all it has at the time of the call is the function itself. – Michael Geary Jul 08 '15 at 17:44
  • And that's why `bind` fixes it. As you can see in Dan's code, the parameter passed to `bind` is a reference to the object you want to use when the function is ultimately called. `bind` holds that reference in a closure and uses it when it calls the original function for you. – Michael Geary Jul 08 '15 at 17:49
  • Thanks! That makes sense. – A. Duff Jul 08 '15 at 18:13