6

How do you stop a timer when clearInterval() does not stop it?

The purpose of this code is to animate a number from 0 upwards until it reaches the end (eg animate from 0... 75%). But the timer does not stop when I call clearInterval():

http://jsfiddle.net/pwYEe/2/

<div id="foo"></div>
<div id="bar"></div>

animate("99%", $("#foo")); // doesnt stop
animate("75%", $("#bar")); // doesnt stop

function loop(clr, clr2, ele, rand, last, delay) {
    clearInterval(clr);
    clearInterval(clr2);
    inloop(clr, clr2, ele, rand, last, delay);

    if (rand >= last) {
        clearInterval(clr);
        clearInterval(clr2);
    }
    else {
        clr2 = setTimeout(function () {
            loop(clr, clr2, ele, rand, last, delay);
        }, 2500);
    }

}
function inloop(clr, clr2, ele, rand, last, delay) {
    ele.html((rand += 1) + "%");

    if (rand >= last) {
        clearInterval(clr);
        clearInterval(clr2);
    }
    else {
        clr = setTimeout(function () {
            inloop(clr, clr2, ele, rand, last, delay);
        }, delay);
    }
}

function animate(end, display) {
    var clr = null;
    var clr2 = null;
    var ele = null;
    var rand = 0;  // initial number
    var last = 99; // last number
    var delay = 5; // inner loop delay

    ele = display;
    ele.html("0%");

    var idx = end.indexOf("%");
    if (idx >=0) {
        end = end.substring(0,idx);
    }
    last = parseInt(end);
    loop(clr, clr2, ele, rand, last, delay);
}
JK.
  • 21,477
  • 35
  • 135
  • 214

1 Answers1

14

You use clearTimeout() with setTimeout().

You don't use clearInterval() with setTimeout(). clearInterval() goes with setInterval().

You also have a structural problem in your code. You are passing around clr and clr2 as arguments and expecting the original values of those to be modified and they are not.

You can fix that by putting them both in an object and passing the object like this:

animate("99%", $("#foo"));
//animate("75%", $("#bar"));

function loop(timers, ele, rand, last, delay) {
    clearTimeout(timers.clr);
    clearTimeout(timers.clr2);
    inloop(timers, ele, rand, last, delay);

    if (rand >= last) {
        clearTimeout(timers.clr);
        clearTimeout(timers.clr2);
    }
    else {
        timers.clr2 = setTimeout(function () {
            loop(timers, ele, rand, last, delay);
        }, 2500);
    }

}
function inloop(timers, ele, rand, last, delay) {
    ele.html((rand += 1) + "%");

    if (rand >= last) {
        clearTimeout(timers.clr);
        clearTimeout(timers.clr2);
    }
    else {
        timers.clr = setTimeout(function () {
            inloop(timers, ele, rand, last, delay);
        }, delay);
    }
}

function animate(end, display) {
    var timers = {clr: null, clr2: null};
    var ele = null;
    var rand = 0;  // initial number
    var last = 99; // last number
    var delay = 5; // inner loop delay

    ele = display;
    ele.html("0%");

    var idx = end.indexOf("%");
    if (idx >=0) {
        end = end.substring(0,idx);
    }
    last = parseInt(end);
    loop(timers, ele, rand, last, delay);
}
​

Working jsFiddle: http://jsfiddle.net/jfriend00/PEqeY/

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • Lol. I just took this question as an example and it uses clearInterval: http://stackoverflow.com/a/4424211/325727. Serves me right for believing anything I read on the internets. Looks good now http://jsfiddle.net/pwYEe/4/ – JK. May 17 '12 at 00:36
  • Correction - that jsFiddle is still not working. It restarts from 0 again after reaching 99%. Not sure why yet – JK. May 17 '12 at 00:40
  • @JK - Please describe what the jsFiddle is supposed to do? – jfriend00 May 17 '12 at 00:43
  • @JK - you also have a structural problem with your code. You are passing clr and clr2 as arguments and expecting the functions you passed them to can modify the originals. They cannot. That will only work with objects or arrays which are passed by reference. timerIDs are passed by value and changing the argument will not affect the original. – jfriend00 May 17 '12 at 00:46
  • Got it, there was no need for loop to call setTimeout(loop,2500) - that's why it was repeating itself. It was supposed to count from 0% to some other number eg 75%. Updated http://jsfiddle.net/pwYEe/5/ – JK. May 17 '12 at 00:47
  • I posted a version that passes the timers in an object so your loop functions can modify the original timers. – jfriend00 May 17 '12 at 00:49
  • Thanks that's a good point about the timers. I've posted an update with your timers edit and the extra setTimeout removed - http://jsfiddle.net/pwYEe/7/ Now works and is coded much better too :) – JK. May 17 '12 at 00:57
  • The solution can also be modified to use the module pattern and keep clr and clr2 in closures so their values are always available where required. – RobG May 17 '12 at 01:05
  • @RobG - Yes to the closure option. I'm quite sure the solution can be done in a lot less code too and a lot more simply, but I didn't really understand what the OP was trying to accomplish with the two different functions so I didn't attempt to simplify it. – jfriend00 May 17 '12 at 02:52
  • @jfriend00—Yes, which is why I didn't attempt it either. – RobG May 17 '12 at 06:22