Settimeout and "This" in JavaScript

Pass correct this context to setTimeout callback?

EDIT: In summary, back in 2010 when this question was asked the most common way to solve this problem was to save a reference to the context where the setTimeout function call is made, because setTimeout executes the function with this pointing to the global object:

var that = this;
if (this.options.destroyOnHide) {
setTimeout(function(){ that.tip.destroy() }, 1000);
}

In the ES5 spec, just released a year before that time, it introduced the bind method, this wasn't suggested in the original answer because it wasn't yet widely supported and you needed polyfills to use it but now it's everywhere:

if (this.options.destroyOnHide) {
setTimeout(function(){ this.tip.destroy() }.bind(this), 1000);
}

The bind function creates a new function with the this value pre-filled.

Now in modern JS, this is exactly the problem arrow functions solve in ES6:

if (this.options.destroyOnHide) {
setTimeout(() => { this.tip.destroy() }, 1000);
}

Arrow functions do not have a this value of its own, when you access it, you are accessing the this value of the enclosing lexical scope.

HTML5 also standardized timers back in 2011, and you can pass now arguments to the callback function:

if (this.options.destroyOnHide) {
setTimeout(function(that){ that.tip.destroy() }, 1000, this);
}

See also:

  • setTimeout - The 'this' problem

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

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().

setTimeout in JS

The main confusion you are having is pretty common and comes from the fact that you are using a loop. Everything outside of the timer callback is JavaScript that is being executed synchronously with no delay. The loop executes 5 times immediately when you run the code and so 5 instances of the timer callback function get placed on the event queue in a matter of milliseconds and it is from that point in time that all 5 timer callbacks are delayed, rather than one callback being delayed from the completion of the prior one.

The first instance of the callback then runs after its initial delay is reached (1 second), then the second instance runs after its delay (2 seconds, which is only 1 second later than when the first function call ran) and then the third one runs (which is only 1 second behind the previous one of 2 seconds) and so on.

What you need to do is place the first instance of the callback on the event queue with a 1 second delay before it runs and then, when that first instance has completed, place another instance on the event queue with a 2 second delay, and so on.

To do this, forget the loop and make the timer function recursive, which will essentially cause a repeating code call, just as a loop would.

let delay = 1000;
let timer = null; // Will hold a reference to the timer

function x() {
timer = setTimeout(function(){
console.log(delay / 1000);
delay += 1000;
if(delay > 5000){
clearTimeout(timer); // Cancel the timer
console.log("Operation Complete!");
} else {
// Because the next call for the parent function comes from within
// the timer callback function, it is delayed until the end of that
// callback function's execution.
x();
}
}, delay);
}
x();

setTimeout executes after for loop in javascript

JS is sync. So all sync code is done first, and all async go in separate thread and they may finish earlier but they have to wait until all sync code is done.

setTimeout(function(){
console.log('setTimeout executes');
},1000); // this function go async in separate thread
for(var i=0;i<10000;i++){
console.log('inside for loop'); // sync
}
console.log('after For Loop'); // sync
// after all sync code async result will be called
// console.log('setTimeout executes'); will happen here

If you want full picture of how JS engines works read this. It is very basic and helps a lot.

how to stop the setTimeout function in javascript in this case?

try this

var myVar = setTimeout(function, milliseconds);
clearTimeout(myVar);

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.

why can't we do call and apply on setTimeout?

From the WHATWG setTimeout documentation:

The setTimeout() method must return the value returned by the timer initialisation steps, passing them the method's arguments, the object on which the method for which the algorithm is running is implemented (a Window or WorkerGlobalScope object) as the method context, and the repeat flag set to false.

setTimeout needs to be called from the context of the window object. The context passed into the .call method is not the window object. To call setTimeout correctly, do:

setTimeout.call(window, callback, delay);

setTimeout function not working with for loop

I got it working with this:
(Thanks Yotam Salmon for your answer, thats what helped me understand what I was doing wrong)

I basically got each function to set the timeout and run the following one, then i got the last one to run the first one again. all i needed then was to start the first one manually then it just ran forever.

function slide1to2(){    document.getElementById('slide1').className = "hidden";    setTimeout(slide2to3, 4000);}function slide2to3(){    document.getElementById('slide2').className = "hidden";    setTimeout(slide3to1, 4000);}function slide3to1(){    document.getElementById('slide1').className = "";    document.getElementById('slide2').className = "";    setTimeout(slide1to2, 4000);}
slide1to2();
div#slideshow{ width:100%; height:50%; background-image:url("white-wall.png");}
div#slideshow div{ width:100%; height:inherit; background-repeat:no-repeat; background-position:center; background-color:rgba(0,0,0,0.5); position:absolute; transition:1s;}
div#slideshow div[id$="1"]{ background:#FF8888; transform:translateX(0%); z-index:3;}
div#slideshow div[id$="2"]{ background:#88FF88; transform:translateX(0%); z-index:2;}
div#slideshow div[id$="3"]{ background:#8888FF; transform:translateX(0%); z-index:1;}
div#slideshow div[id$="1"].hidden{ transform:translateX(-100%);}
div#slideshow div[id$="2"].hidden{ transform:translateX(-100%);}
<div id="slideshow">    <div id="slide1"></div>    <div id="slide2"></div>    <div id="slide3"></div></div>

JavaScript do something during the settimeout

Just place the code immediately after the call to setTimeout():

setTimeout(function(){
// Code to be executed after timeout goes here
}, 10000);
// Code to be executed immediately goes here


Related Topics



Leave a reply



Submit