1

I'm trying to create a countdown timer that counts down from a given time and when it reaches 0, it starts counting back up again.

My code works (after a lot of trial and error) however it's losing the first second after being initiated.

My thinking is that it's not an optimized solution and it's taking a second or more to initiate.

Is there a better way to achieve this?

Javascript:

function startCountdown() {
        var setPlayers = ['Name 1', 'Name 2', 'Name 3'];
        var playersIndex = 0;
        var countdownState = 0;
        var time = 5;
        var messageEl = document.getElementById('message');
        var timer = document.getElementById('timer');

        var standUp = setInterval(function(){

            // if countownState = 0, count down.
            if(countdownState == 0){

                // if all players have been, end the timer
                if(playersIndex == setPlayers.length){
                    clearInterval(standUp);
                    messageEl.innerHTML = 'Finished!';
                    timer.innerHTML = '';

                // if time reaches 0 switch the state and start counting up
                } else if(time == 0) {
                    countdownState = 1;
                    timer.innerHTML = pad(parseInt(time/60,10)) + ':' + pad(++time%60);

                // usual countdown 
                } else {
                    timer.innerHTML = pad(parseInt(time/60,10)) + ':' + pad(--time%60);
                }
            // if countownState = 1, count up.
            } else {
                timer.innerHTML = pad(parseInt(time/60,10)) + ':' + pad(++time%60);
            }
        }, 1000);
    }

I'll have a seperate function that increments the player index and starts the timer again.

HTML

<div id="timer"></div>
<div id="message"></div>

When I load the page, the timer looks like it is starting from 4 instead of 5.

Nick
  • 51
  • 2
  • 1
    Please add the relevant HTML so we can replicate your issue. Also, don't use `.innerHTML` when the string doesn't contain any HTML. Use `.textContent` instead. – Scott Marcus Mar 27 '19 at 20:50
  • Also, understand that `setInterval()` and `setTimeout()` don't guarantee a time to wait until your function runs since JavaScript is single-threaded. The time you provide is the absolute minimum amount of time you might have to wait. If you need more precision, try Moment.js. – Scott Marcus Mar 27 '19 at 20:52
  • How do you know it is losing a second? what do you see and what are you expected to see? – Huangism Mar 27 '19 at 20:52
  • 1
    `setInterval()` waits whatever time you put in, then keeps going. You have it set to 1000ms, which is equal to 1s. Hence why it starts 1s late, as you put it. `setTimeout(function, milliseconds)` Executes a function, after waiting a specified number of milliseconds. `setInterval(function, milliseconds)` Same as setTimeout(), but repeats the execution of the function continuously. – Rob Scott Mar 27 '19 at 20:54
  • If what Rob says is indeed the issue, then see [Execute the setInterval function without delay the first time](https://stackoverflow.com/questions/6685396/execute-the-setinterval-function-without-delay-the-first-time). – Ivar Mar 27 '19 at 20:55
  • Possible duplicate of [Execute the setInterval function without delay the first time](https://stackoverflow.com/questions/6685396/execute-the-setinterval-function-without-delay-the-first-time) – imjared Mar 27 '19 at 21:02
  • The easy solution is to move the function out of your setInterval call. Call that function directly and in your setInverval call. – Noah B Mar 27 '19 at 21:02
  • I've updated the post with the suggested edits and clarifications. I'll try ```setTimeout(function, milliseconds)``` instead. – Nick Mar 27 '19 at 21:05
  • Upon further reading - https://javascript.info/settimeout-setinterval - your code consumes part of the interval. Here's also a very good explanation of real time counters in JS -- https://stackoverflow.com/questions/20618355/the-simplest-possible-javascript-countdown-timer – Rob Scott Mar 27 '19 at 21:05
  • 1
    "_the timer looks like it is starting from 4 instead of 5._" Well it is. Using `++time` and `--time` will first increment/decrement the value and then print it. You set it originally to 5 so the first it prints will be 4. If you want to first print the value and then increment/decrement, you'll need to use the postfix increment/decrement operator (`time++`/`time--`). – Ivar Mar 27 '19 at 21:07
  • 1
    @Ivar, (```time++/time--```) made it function as expected. – Nick Mar 27 '19 at 21:13

1 Answers1

1

I updated your interval call to be a separate function. This will require all of the variables to be moved out of the startCountdown function so that they are globals. There are better ways to do this, but it will solve your problem.

var setPlayers = ['Name 1', 'Name 2', 'Name 3'];
var playersIndex = 0;
var countdownState = 0;
var time = 5;
var messageEl = document.getElementById('message');
var timer = document.getElementById('timer');

function startCountdown() 
{

    countDown();
    var standUp = setInterval(countDown, 1000);
}

function countDown()
{
    // if countownState = 0, count down.
    if(countdownState == 0)
    {

        // if all players have been, end the timer
        if(playersIndex == setPlayers.length)
        {
            clearInterval(standUp);
            messageEl.innerHTML = 'Finished!';
            timer.innerHTML = '';

            // if time reaches 0 switch the state and start counting up
        } 
        else if(time == 0) 
        {
            countdownState = 1;
            timer.innerHTML = pad(parseInt(time/60,10)) + ':' + pad(++time%60);

            // usual countdown 
        } 
        else 
        {
            timer.innerHTML = pad(parseInt(time/60,10)) + ':' + pad(--time%60);
        }
        // if countownState = 1, count up.
    } 
    else 
    {
        timer.innerHTML = pad(parseInt(time/60,10)) + ':' + pad(++time%60);
    }
}
Noah B
  • 193
  • 6