user3822370
user3822370

Reputation: 647

How can an instance of a timeout function be exited?

Let's say you want to create a java function with setTimeout so that it repeats every 5 seconds. This timed function is just updating some data. The call occurs when the users clicks a button that opens an overlay, and the function will continue to run as long as the overlay is open. abandonUpdate is set to true once the overlay is closed, and this is checked in the timer function. However if the user opens the overlay before the timeout completes, it will add on another instance of the timeout function.

How can this be avoided?

http://jsfiddle.net/m6LAL/1/

(By the way the timer does not seem to work on jsfiddle or bin.. the same code works on my server however.. )

var abandonUpdate = true;

function timers(){
    if(!abandonUpdate){
        $('.timeout').css({"background-color":'#'+(Math.random()*0xFFFFFF<<0).toString(16)});
        setTimeout("timers()", 5000);
    }
}

$('.timeout').click(function(){
    abandonUpdate = false;
    console.log("Hello I'm running");
    timers(); 
});

Upvotes: 0

Views: 100

Answers (2)

Jared Farrish
Jared Farrish

Reputation: 49218

Let look at a straight demonstration:

var loops = 0,
    to = setTimeout(function test(){
        console.log('Test run ' + ++loops + ' times.');

        if (loops < 100) {
            to = setTimeout(test, 3000);
        }
    }, 3000),
    check = setTimeout(function check(){
        console.log('Test checked (' + loops + ').');

        if (loops > 10) {
            console.log('Test setTimeout aborted.');

            clearTimeout(to);

            return;
        }

        setTimeout(check, 500);
    });

What we have here are two regenerating setTimeout()s, each of which does something slightly different. The first, using the var to, runs the test() function, which is what we're trying to preempt (or abort, if you will).

The second, var check, runs more frequently (1/2 second as opposed to 3 seconds). The second is monitoring the iterations of test() and "cancels" (or "clears") the test() timeout, using the captured reference var to.

Note how I've captured the reference within test() when it regenerates the timeout? If you don't do this, you get an infinite loop; each reference returned by setTimeout() changes, so you have to be careful to capture the reference to the same variable that both functions have access to. Otherwise, you "clear" the timeout of one that has possibly/probably already ended.

The alternative, of course, is to use setInterval(), which always uses the same reference when it runs, and thus clearInterval(to) would work and you don't have to worry about keeping to set to the right reference. I would consider this preferential; use setInterval() here, not setTimeout(). There are times it makes sense to regenerate, but I don't think it does here.

var intl;

function timers() {
    $('.timeout').css({
        "background-color": '#' + (Math.random() * 0xFFFFFF << 0).toString(16)
    });
}

$('.timeout').click(function(){
    if (intl) {
        clearInterval(intl);
        console.log("I stopped goodbye!");
        intl = 0;
    } else {
        console.log("Hello I'm running!");
        intl = setInterval(timers, 5000); // Pass the test reference, not "test()";
    }
});

http://jsfiddle.net/m6LAL/2/

Upvotes: 2

Jaime Gomez
Jaime Gomez

Reputation: 7067

I would recommend using setInterval, so you don't need to keep recreating the timeout; save the return value, and use clearInterval when the overlay closes:

var interval = setInterval(work, 1000)
close_button.onclick = function() {
    clearInterval(interval)
}

Working example.

And as @Jared mentions, javascript is not the same as Java :) fortunately...

Upvotes: 1

Related Questions