Settimeout() Inside JavaScript Class Using "This"

setTimeout() inside JavaScript Class using this

You can do this:

 var that = this;
setTimeout(function () {
that.doStuff();
}, 4000);

You can also bind for more succinct code (as originally pointed out by @Raynos):

setTimeout(this.doStuff.bind(this), 4000);

bind is a standard library function for exactly this coding pattern (ie capturing this lexically).

Access to class variable in setTimeout function

You can use these 2 approaches. I think first is the best if you have nothing to do with function own context and it is more preferable approach.

1) Arrow function - this.timeout = setTimeout(() => this.startWarning(), 30000);

2) Explicit Context binding - this.timeout = setTimeout(this.startWarning.bind(this), 30000);

Using setTimeout() within a JavaScript class function

The problem with the code is that in JavaScript, this is set (in the normal case) by how a function is called, not where it's defined. This is different than some other languages you might be used to such as Java or C#. So this line:

setTimeout(this.animate, 40);

...will indeed call your animate function, but with this set to the global object (window, on browsers). So all of those properties you're accessing (this.running, etc.) will not be looking at your object, but rather looking for those properties on window, which is clearly not what you want.

Instead, you can use a closure:

var me = this;
setTimeout(function() {
me.animate();
}, 40);

That works because the anonymous function we're giving to setTimeout is a closure over the context in which it's defined, which includes the me variable we're setting up before defining it. By calling animate from a property on the object (me.animate()), we're telling JavaScript to set up this to be the object during the call.

Some frameworks have methods to create this closure for you (jQuery has jQuery.proxy, Prototype has Function#bind), and ECMAScript 5 (about 18 months old) defines a new Function#bind feature for JavaScript that does it. But you can't rely on it yet in browser-based implementations.

More discussion and solutions here: You must remember this


Possibly off-topic: In your code, you're using a lot of named function expressions. E.g.:

this.animate = function animate() { ... };

Named function expressions don't work correctly on IE prior to, I think, IE9. IE will actually create two completely separate functions (at two separate times). More here: Double-take


Update and a bit off-topic, but since all of your functions are defined as closures within your AnimateManager constructor anyway, there's no reason for anything you don't want to be public to be public, and you can completely get rid of issues managing this.

Here's the "solution" code from your updated question, making use of the closures you're already defining to avoid this entirely other than when defining the public functions. This also uses array literal notation for shapes and a normal for loop (not for..in) for looping through the array (read this for why: Myths and realities of for..in):

var shapes = [
new Shape(0,0,50,50,10)),
new Shape(0,100,100,50,10)),
new Shape(0,200,100,100,10))
];

/**
* AnimationManager class
* animate() runs the animation cycle
*/
var AnimationManager = function(canvas)
{
var canvasWidth = canvas.width(),
canvasHeight = canvas.height(),
ctx = canvas.get(0).getContext('2d'),
running = true, // Really true? Not false?
me = this;

// Set up our public functions
this.start = AnimationManager_start;
this.run = AnimationManager_run;
this.stop = AnimationManager_stop;

/** Start the animations **/
function AnimationManager_start(){
running = true;
animate();
}

/** Allow the animations to run */
function AnimationManager_run(){
running = true;
}

/** Stop the animations from running */
function AnimationManager_stop(){
running = false;
}

/** Internal implementation **/
function animate()
{
if (running)
{
update();
clear();
draw();
}

setTimeout(animate, 40); //25fps
}

/** Update all of the animations */
function update()
{
var i;

for (i = 0; i < shapes.length; ++i) // not for..in
{
shapes[i].moveRight();
}
}

/** Clear the canvas */
function clear()
{
ctx.clearRect(0,0, canvasWidth, canvasHeight);
}

/** Draw all of the updated elements */
function draw()
{
var i;

for (i = 0; i < shapes.length; ++i) // not for..in
{
ctx.fillRect(shapes[i].x, shapes[i].y, shapes[i].w, shapes[i].h);
}
}
}

Each object created via new AnimationManager will get its own copy of the local variables within the constructor, which live on as long as any of the functions defined within the constructor is referenced anywhere. Thus the variables are truly private, and instance-specific. FWIW.

calling this._super() from inside setTimeout()

To make this work you need to capture the _super method while in the subclassed method, like this:

dance: function(){
// capture the super method for later usage
var superMethod = this._super;
window.setTimeout(function(){
return superMethod();
},50);
};

The reason this works, and your code does not, is that the extend() method in inherit.js captures the superclass'
method as this._super right before your overridden method is run. It then runs your code, and after your code has
run it restores _super to whatever it was set to before it ran. The action takes place in the following bit of code from inherit.js

      var tmp = this._super;

// Add a new ._super() method that is the same method
// but on the super-class
this._super = _super[name];

// The method only need to be bound temporarily, so we
// remove it when we're done executing
var ret = fn.apply(this, arguments);
this._super = tmp;

return ret;

So to be more specific; when the original code was run, the function that was used as a parameter to setTimeout was bound to the original object. The reason it did not work was that, even though this referenced the right object, this._super referenced something else, since this._super was reset to point to whatever it pointed to before the method was run. Probably it was not set, so the value of this._super was most likely just undefined.

Class method that uses setTimeout() and setInterval() works unexpectedly on repeated calls

The problem in is in trying to clear a timeout in Node (where I believe a timeout returns an object of properties rather than just an incremental number as usual). A solution I have found is clearing the timeout in the usual way and then reassigning the timeout variable to an empty object. It works though it might be a bit messy and I would certainly be interested in other solutions.

const callback = () => {
//body of function here
clearTimeout(myTimeout);
myTimeout = {};
}
const myTimeout = setTimeout(callback,1000);

setTimeout and this in JavaScript

The issue is that setTimeout() causes javascript to use the global scope. Essentially, you're calling the method() class, but not from this. Instead you're just telling setTimeout to use the function method, with no particular scope.

To fix this you can wrap the function call in another function call that references the correct variables. It will look something like this:

test.protoype.method = function()
{
var that = this;

//method2 returns image based on the id passed
this.method2('useSomeElement').src = "http://www.some.url";

var callMethod = function()
{
that.method();
}

timeDelay = window.setTimeout(callMethod, 5000);
};

that can be this because callMethod() is within method's scope.

This problem becomes more complex when you need to pass parameters to the setTimeout method, as IE doesn't support more than two parameters to setTimeout. In that case you'll need to read up on closures.

Also, as a sidenote, you're setting yourself up for an infinite loop, since method() always calls method().

Property of `this` is undefined inside a setTimeout

When functions are passed by reference, they lose their reference to this. You're losing this reference when calling setTimeout.

Functions have a bind() method that basically return a new function with a corrected reference to this.

Call it as such:

setTimeout(this.onLoop.bind(this), 1000)

Alternatively, you can also pass an in-line arrow function. Arrow functions don't lose their this context.

setTimeout(() => this.onLoop(), 1000)

How to setTimeout for one of css classes of HTML element?

Since you say you are not expert in javascript and are studying php/html/css, you may try with CSS animation properties. Be aware that animation is an advanced topic in general. A better solution might be the javascript one that Mister Jojo provided.

I could understand that:

  • you can only put the class in the resulting html
  • you need the change to happen after 3 seconds
  • the change must happen only once
  • the change regards the background color

Following there is a possible implementation, based on the previous assumptions.

.first {
background-color: lightblue;
}

.second {
animation-delay: 3s;
animation-duration: 1ms;
animation-fill-mode: forwards;
animation-iteration-count: 1;
animation-name: delayChangeColor;
}

@keyframes delayChangeColor {
to {
background-color: red;
}
}
<div class="first">1</div>
<div class="first second">2</div>
<div class="first">3</div>


Related Topics



Leave a reply



Submit