Variable inside setTimeout says it is undefined, but when outside it is defined

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.


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!


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));

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(() => {
               that.http.post(...  //http is not defined here
        }
   }

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/