Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

javascript timer or intervals created in loop using closure

I'm using jQuery to setup a timer or interval loop on a few elements to check them every couple seconds. I've tried setting a timer and checking if I should restart it, or setting and interval and checking if I should stop it.

Although simplified, this is basically what I need:

var mytimers = new Array();
$('div.items').each(function() {
    myID = $(this).attr('id');
    mytimers[myID] = setInterval( function() { myFunction(myID) } , 3000)
});
function myFunction(param) {
    alert(param);
    if (something()) {
        clearInterval(mytimers[param]);
    }
}

The ID's for class items are id_1, id_2, id_3. But I simply get 3 alerts all giving id_3. In my code I started off trying to pass 'this', but kept simplifying it to figure out the issue.

How can I get it to copy the variable to a new address each time? I know I need to use closures. It seems to be referencing the other var no mater what.

I tried simplifying it to a loop with timers like so:

function tester(item) {
    return function() {
        alert(item);
    };
}
for(x=1;x<=3;x++) {
    setTimeout( '(function() { tester(x) })(x)' , 3000);
}

But I think I'm just making my problem worse and that doesn't seem to do anything.

I've searched for previous questions, but most are filled with tons of extra code rather than cutting it down the the specific issue and are solved in some other manner. I'd like to understand how this works better by getting this example working. I did manage, while writing this, to figure out I can set off the timer with a helping function.

function tester(item)
    alert(item);
function myTimer(item)
    setInterval( function() { tester(item); }, 3000);
for(x=1;x<=3;x++)
    myTimer(item);

How can this be done without that? Is there some better way?

like image 344
phazei Avatar asked Jul 20 '10 20:07

phazei


People also ask

Why JavaScript timer is unreliable?

The reason why this happens is because Javascript is single threaded.

How do you end a timer in JavaScript?

Clicking the Stop button clears the timer by using the clearInterval(), passing in the 'check' variable that is returned by the call to setInterval(). The last step is to reset the value of the counter to '0'.

What is difference between setTimeout and setInterval in JavaScript?

setTimeout allows us to run a function once after the interval of time. setInterval allows us to run a function repeatedly, starting after the interval of time, then repeating continuously at that interval.


3 Answers

Have variable 'myID' local to the anonymous function,

var myID = $(this).attr('id');
like image 129
Marimuthu Madasamy Avatar answered Oct 16 '22 00:10

Marimuthu Madasamy


You're in a closure when you use each, you just have forgotten var to make your variable private in the function scope

var mytimers = new Array();
$('div.items').each(function() {
    **var** myID = $(this).attr('id'); 
    mytimers[myID] = setInterval( function() { myFunction(myID) } , 3000)
});
function myFunction(param) {
    alert(param);
    if (something()) {
        clearInterval(mytimers[param]);
    }
}
like image 40
Andreas Köberle Avatar answered Oct 16 '22 00:10

Andreas Köberle


So you want to run myFunction on each of the matched elements every three seconds?

Try this:

$('div.items').each( myFunction(this) );

var myFunction = function(elem) {
    return function() {
        if ( something(elem) ) {
            //return or do something with elem
        } else {
            window.setTimeout( myFunction(elem), 3000 );
        }
    }
};

If the condition in something() is met you're done, if not the function schedules itself to run again in 3 seconds with the same element as before. You can call this as many times as you want on different elements, each call has its own elem.

I prefer to pass objects like elem around and delay working with their internals until the last possible moment. Let something() worry about the id or whatever.

None of the other gymnastics with the associative array (instead of new Array() you could just use {}), or clearInterval, or ID's are necessary.

To actually address your solution, as Marimuthu said, you left var off the declaration of myID, which means its global and it gets overwritten each iteration. The result is that when setInterval invokes myFunction instead of getting a unique local myID via closure, you get the global one that has already been overwritten umpty times.

like image 32
jasongetsdown Avatar answered Oct 15 '22 22:10

jasongetsdown