0

I am trying to run sequential countdown timers but can't figure out how to wait for the timer to finish before moving onto the next item.

for(var i = 0; i < 5; i++)
{
    var count = 5;
    var counter = setInterval(timer, 1000);
}

function timer()
{
    count--;
    if (count <= 0)
    {
        $('.workout-timer').text(count + "secs");
        clearInterval(counter);
        return;
    }

    $('.workout-timer').text(count + "secs");
}

This just goes into negative, however without the for loop the code counts down from 5 to 0 just fine. So my question is how would get several countdowns one after another? Is the timer not the right way to go about it?

MarthyM
  • 1,839
  • 2
  • 21
  • 23
user975044
  • 375
  • 4
  • 11
  • 26
  • Don't create 5 intervals. Create one (without a loop). – user2246674 May 14 '13 at 05:45
  • why not use `setTimeout`? – akonsu May 14 '13 at 05:46
  • @user2246674 what does this mean? – akonsu May 14 '13 at 05:47
  • @akonsu "why not use setTimeout?" <-- Justify, please. If I'm eating peanut butter, tell me why I should switch to jam. – user2246674 May 14 '13 at 05:48
  • the original poster wants to run timers in a succession and cancels them when the handler is called. this is what `setTimeout` is for. – akonsu May 14 '13 at 05:50
  • @akonsu The original code has a flaw because the setInterval is created in a loop and only the last interval is stopped. However, setInterval works perfectly fine, as I show in my answer. – user2246674 May 14 '13 at 05:57
  • I think I know what the OP wants. a timer with an associated counter which (a counter) is not global, but in a closure perhaps. so several such timers could be used simultaneously. – akonsu May 14 '13 at 05:59
  • but my last comment did not mention timeouts vs intervals :) – akonsu May 14 '13 at 06:01
  • i want to have several countdowns happen back to back, given the number of objects I will be passing to the loop. so I could have 4 objects that need to countdown one after another from 5-4-3-2-1-0 – user975044 May 14 '13 at 06:01
  • @user2246674 `setInterval` with small interval could easily "misbehave" if you have other stuff running which consumes execution time/cpu. In that case the calls will be "remembered" until there is time to execute them - all at "once". Have a look at [this](http://stackoverflow.com/a/731625/402037) – Andreas May 14 '13 at 06:02
  • @user975044 Then create each task after the previous task completes. – user2246674 May 14 '13 at 06:02
  • @Andreas Very good link - that *is* justification for using one over the other, depending upon the need. – user2246674 May 14 '13 at 06:03

2 Answers2

1

You could do something like this:

function startCountdown(count, delay, callback) {
    if (!count) {
        callback && callback();
        return;
    }

    //do something here
    console.log(count);

    setTimeout(function () {
        startCountdown(--count, delay, callback);
    }, delay);
}

startCountdown(5, 1000, function () {
    startCountdown(5, 1500);
});

However this can get messy if you have a lot of nested callbacks, but here's one out of many approach you could use to deal with that issue:

var queue = [
        { count: 5, delay: 1000 },
        { count: 10, delay: 200 },
        { count: 5, delay: 5000 }
    ];

processNextCountdown();

function processNextCountdown() {
    var options = queue.shift();

    if (options) {
        startCountdown(options.count, options.delay, processNextCountdown);
    }
}
plalx
  • 42,889
  • 6
  • 74
  • 90
1

Intervals are like timeouts that will reschedule themselves (which differs from a timeout starting a new timeout). Since intervals reschedule themselves, only create one. (Or, only as many as really necessary.)

The problem with the original post is it was creating 5 intervals (because they were being created in the loop) and then only keeping the interval ID (in counter) of the last interval created! Thus the clearInterval only stopped the last interval and the other 4 intervals kept running and running and running ..

Here is some cleaned up code with comments and without the original problem:

var count = 5;
// only need ONE interval
var counter = setInterval(timer, 1000);
// so we do one count RIGHT NOW
timer();

function timer() {
  // display first, so we start at 5: 5, 4 .. 1
  console.log(count);
  count--;
  if (count < 0) {
    // to repeat the count, comment out the clearInterval
    // and do `count = 5;` or similar .. take it from here :D
    clearInterval(counter);
  }
}

To create separate "state" for each countdown, either create a new countdown object that maintains state in properties or use a closure. Here is an example with a closure. I have also added support for a callback function to show how such a function can be made more generic:

function makeCountdown(startCount, delay, fn) {
    fn = fn || function (i) {
       // default action, if fn not specified
       console.log(i);
    };
    // local variables
    var count = startCount;
    var counter = setInterval(timer, delay);
    timer();

    function timer() {
        // now count and counter refer to variables in the closure (keyword!)
        // which are different each time makeCountdown is called.
        fn(count);
        count--;
        if (count < 0) {
            clearInterval(counter);
        }
    }
}

makeCountdown(20, 500); // uses default function
makeCountdown(10, 1000, function (i) { console.log(10 - i) });
makeCountdown(5, 2000, function (i) { console.log("SLOW! " + i) });

Exercises:

  1. Add a callback function for when the countdown is "done" so that countdowns can be run in series.
  2. Consume a series generator and use that to generate the next count value.
  3. Have makeCountdown return an object that can be used to control the countdown.
  4. Have fun!
Community
  • 1
  • 1
user2246674
  • 7,621
  • 25
  • 28
  • this doesn't address the issue I have, I am actually trying to get 5 separate countdowns that happen one after another – user975044 May 14 '13 at 06:03
  • @user975044 Updated with an example that uses closures to remember the "count" and "counter" variables per interval. – user2246674 May 14 '13 at 06:12
  • @user975044 This can get as fun/complex/generic as needed. jQuery has a queue implementation for dealing with animations - e.g. move left 20, then fade out – user2246674 May 14 '13 at 06:14