43

I have a class. I need to do some http work inside of a timeout. The problem I am faceing is the http variable inside the timeout keeps saying it is undefined.

export class MyClass {

    http:Http:

    constructor(private http:Http) {
        this.http = http;
    }

    sendFriendRequest(){

    this.http.post( ...//http variable is defined here
           setTimeout(function(){
               this.http.post(...  //http is not defined here
        }
   }
}
3
  • 8
    It's because the this context: stackoverflow.com/questions/2130241/…
    – nikoskip
    Commented Mar 24, 2016 at 20:53
  • 5
    Do we really need 4 answers to this question that all essentially say "use the arrow function to get the proper this context"? Commented Mar 24, 2016 at 21:47
  • 3
    @MarkRajcok yes, with 3 answers is not enough! Commented Mar 24, 2016 at 22:11

4 Answers 4

82

The reason for this is that the callback function inside setTimeout is in a different lexical environment. This is why in ES6+ functions can be defined using =>. This is so that the code within a function shares the same scope as the function.

To fix this, you can either use ES6+ syntax, where instead of function(a,b,args){...} you would use (a,b,args) => {...}:

setTimeout(() => {
  this.http.post(...);
});

or with ES5 syntax:

var root = this;

setTimeout(function(){
    root.http.post(...);
});

Hope this helps!

2
  • Will it work if in 2nd example we replace function() with arrow function () =>? If not, why?
    – pbn
    Commented Jan 24, 2019 at 9:29
  • @pbn yes it will work, since arrow function don't have their own binding 'this' will represent the enclosing execution context Commented Sep 22, 2022 at 10:41
21

In JavaScript the this keyword is used to access the context in which a function is invoked. Functions in JavaScript are always invoked with a context whether you invoke them using the .methodName() syntax or without it, unless the 'use strict' flag is set in the current scope.

When a function is invoked without a context like this:

myFunction()

the context is assumed by the runtime to be the global window object (unless the 'use strict' flag is set, in which case the context will be undefined.)

Note: When using ES6 with a transpiler like Babel, strict mode is set by default in the output.

When a reference to a function is saved on an object, you can invoke that function with the object as the context of 'this' using the dot syntax.

var myObj = {
    myFunc: function(){}
};

// myFunc invoked like this, the value of 'this' inside myFunc will be myObj.
myObj.myFunc();

Manipulate 'this':

Call and Apply

You can always change the context of a function by invoking it with the .call or .apply methods. In this case you have an anonymous function which is not invoked by you, but rather is invoked by the setTimeout function. Because of that, you won't be able to take advantage of .call or .apply.

Bind

Instead, you can create a new function that has a custom context by using the .bind method. By invoking .bind() on your anonymous function, an new function will be returned which has your custom context bound to 'this'. That way you can pass your custom bound function as data to setTimeout.

setTimeout(function(){
   // your code.
}.bind(this), 1000);

now inside the anonymous function the 'this' keyword would be bound to the correct value.

Lexical 'this':

In ES6 however, when using an arrow function the rules about 'this' change. If you use this syntax you will see the context of 'this' will stay the same as whatever it is in the current scope.

setTimeout(() => {
    // Hey I can access 'this' in here!
}, 1000);

Saving a reference:

If you look at the compiled output from Babel you will see Babel keeps track of the context by saving references to 'this' with _this1, _this2 and so forth.

To use this method yourself simply declare a new variable ( it's common to use 'that' or 'self' ) and access the value using it inside your anonymous function like so:

var self = this;
setTimeout(function(){
    self.http.post...
}); 

Hope this helps.

For more explanation developer.mozilla.org has a good article describing the behavior of 'this' inside a functions scope.

2
  • Thanks for the great response! Commented Mar 24, 2016 at 21:46
  • perfect answer with explanation +1 :) Commented Jan 2, 2020 at 6:32
8

You should use arrow function here, to preserve existense of this.

setTimeout(()=>{
   this.http.post(...  //http is not defined here
})

In doing so, this inside of the function is bound to the outer context. It is the same as:

setTimeout(function(){
    this.http.post();
}.bind(this));
0
1

it's not same this inside setTimeout when you use function(){...

2 most popular ways for this issue:

  1. use extra variable to store outside "this"

    var that = this; this.http.post( ...//http variable is defined here setTimeout(function(){ that.http.post(... //http is not defined here } }

  2. use arrow functions

    this.http.post( ...//http variable is defined here setTimeout(() => { this.http.post(... //http is not defined here } }

--- Update

Currently most of environment do support arrow functions natively

1st way is old ES5 and you don't need any compilers, for ES6 version (#2) you will need to use something like babel.

find more about arrow functions & babel here: https://babeljs.io/docs/learn-es2015/

1
  • Thanks for the info! Commented Mar 24, 2016 at 21:47

Not the answer you're looking for? Browse other questions tagged or ask your own question.