Why is 'this' undefined inside class method when using promises?
Solution 1
this
is always the object the method is called on. However, when passing the method to then()
, you are not calling it! The method will be stored somewhere and called from there later. If you want to preserve this
, you will have to do it like this:
.then(() => this.method2())
or if you have to do it the pre-ES6 way, you need to preserve this
before:
var that = this;
// ...
.then(function() { that.method2() })
Solution 2
Promise handlers are called in the context of the global object (window
) by default. When in strict mode (use strict;
), the context is undefined
. This is what's happening to method2
and method3
.
;(function(){
'use strict'
Promise.resolve('foo').then(function(){console.log(this)}); // undefined
}());
;(function(){
Promise.resolve('foo').then(function(){console.log(this)}); // window
}());
For method1
, you're calling method1
as this.method1()
. This way of calling it calls it in the context of the this
object which is your instance. That's why the context inside method1
is the instance.
Solution 3
Basically, you're passing it a function reference with no context reference. The this
context is determined in a few ways:
- Implicitly. Calling a global function or a function without a binding assumes a global context.*
- By direct reference. If you call
myObj.f()
thenmyObj
is going to be thethis
context.** - Manual binding. This is your class of functions such as
.bind
and.apply
. These you explicitly state what thethis
context is. These always take precedence over the previous two.
In your example, you're passing a function reference, so at it's invocation it's implied to be a global function or one without context. Using .bind
resolves this by creating a new function where this
is explicitly set.
*This is only true in non-strict mode. In strict mode, this
is set to undefined
.
**Assuming the function you're using hasn't been manually bound.
Solution 4
One way functions get their context (this
) is from the object on which they are invoked (which is why method1
has the right context - it's invoked on this
). You are passing a reference to the function itself to then
. You can imagine that the implementation of then
looks something like this:
function then( callback ) {
// assume 'value' is the recently-fulfilled promise value
callback(value);
}
In that example callback
is a reference to your function. It doesn't have any context. As you've already noted you can get around that by binding the function to a context before you pass it to then.
SteamDev
Updated on May 23, 2020Comments
-
SteamDev almost 4 years
I have a javascript class, and each method returns a
Q
promise. I want to know whythis
is undefined inmethod2
andmethod3
. Is there a more correct way to write this code?function MyClass(opts){ this.options = opts; return this.method1() .then(this.method2) .then(this.method3); } MyClass.prototype.method1 = function(){ // ...q stuff... console.log(this.options); // logs "opts" object return deferred.promise; }; MyClass.prototype.method2 = function(method1resolve){ // ...q stuff... console.log(this); // logs undefined return deferred.promise; }; MyClass.prototype.method3 = function(method2resolve){ // ...q stuff... console.log(this); // logs undefined return deferred.promise; };
I can fix this by using
bind
:function MyClass(opts){ this.options = opts; return this.method1() .then(this.method2.bind(this)) .then(this.method3.bind(this)); }
But not entirely sure why
bind
is necessary; is.then()
killingthis
off? -
Philip Murphy over 7 yearsgreat answer - or pre-ES6 ".then(this.method2.bind(this))"
-
mateos over 6 yearsI used
.then(data => this.method(data))
-
Hobbamok over 5 yearsFAR FAR easyer way: .then(( foo = this) => {... //then use foo instead of this [also translates to the pre-es6 version] Just make sure it's not overwridden!
-
Remus.A over 4 yearsunderrated explanation, struggled with this quite a bit when setting up promises, now fixex. thanks
-
Volmarg Reiso over 2 yearsNow this is an answer that helped me to understand it.