Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

setTimeout scope issue

Tags:

javascript

I have a setTimeout defined inside of a function that controls the player's respawn (i am creating a game):

var player = {     ...     death:(function() {         this.alive = false;         Console.log("death!");         var timer3 = setTimeout((function() {             this.alive = true;             Console.log("alive!");         }),3000);     }),     ... } 

When it executes, I read in the console, "death!" and 3 seconds later "alive!". However, alive is never really set back to true, because if i write player.alive in the console, it returns false. How come i can see "alive!" but the variable is never set back to true?

like image 274
Polyov Avatar asked Jul 30 '12 01:07

Polyov


People also ask

Does setTimeout affect performance?

No significant effect at all, setTimeout runs in an event loop, it doesn't block or harm execution.

Is setTimeout reliable?

No, you can't be absolutely certain of anything, and javascript timers aren't very accurate either, but most of the time they are accurate enough, and will execute the function close enough to the set time.

Is there a limit to setTimeout?

Browsers including Internet Explorer, Chrome, Safari, and Firefox store the delay as a 32-bit signed integer internally. This causes an integer overflow when using delays larger than 2,147,483,647 ms (about 24.8 days), resulting in the timeout being executed immediately.

Is JavaScript setTimeout blocking?

Explanation: setTimeout() is non-blocking which means it will run when the statements outside of it have executed and then after one second it will execute. All other statements that are not part of setTimeout() are blocking which means no other statement will execute before the current statement finishes.


2 Answers

You have to be careful with this. You need to assign your this in the outer scope to a variable. The this keyword always refers to the this of the current scope, which changes any time you wrap something in function() { ... }.

var thing = this; thing.alive = false; Console.log("death!"); var timer3 = setTimeout((function() {     thing.alive = true;     Console.log("alive!"); }),3000); 

This should give you better success.

Update 2019-10-09: The original answer is true, but another option is now available for recent versions of JavaScript. Instead of using function, you can use an arrow function instead, which does not modify this:

this.alive = false; Console.log("death!"); var timer3 = setTimeout(() => {     this.alive = true;     Console.log("alive!"); }), 3000); 

This is supported from ES6 forward, which is part of all current browsers but IE (of course), I think. If you are using a modern framework to build your project via Babel or whatever, the framework should make sure this works as expected everywhere.

like image 170
zostay Avatar answered Oct 07 '22 17:10

zostay


It's because this in the setTimeout handler is referring to window, which is presumably not the same value as referenced by this outside the handler.

You can cache the outer value, and use it inside...

var self = this;  var timer3 = setTimeout((function() {     self.alive = true;     Console.log("alive!"); }),3000); 

...or you can use ES5 Function.prototype.bind...

var timer3 = setTimeout((function() {     this.alive = true;     Console.log("alive!"); }.bind(this)),3000); 

...though if you're supporting legacy implementations, you'll need to add a shim to Function.prototype.

  • MDN Function.prototype.bind patch

...or if you're working in an ES6 environment...

var timer3 = setTimeout(()=>{     this.alive = true;     Console.log("alive!"); },3000); 

Because there's no binding of this in Arrow functions.

like image 31
2 revs, 2 users 67%user1106925 Avatar answered Oct 07 '22 18:10

2 revs, 2 users 67%user1106925