0

I am working on a timer that runs for a set amount of minutes, then starts over for a break period that counts down and then goes back to the original amount of minutes. I'm struggling with the logic. So far, I have it running down the original time, then running down the break timer but I need help making it return to the original time and loop this infinitely (or until the stop button is pressed). Here's what I have:

    function timer(minutes, breakLength) {

    --minutes;

    timerId = setInterval(function() {

        if (minutes >= 0) {

            if (seconds > 0) {
                --seconds;
            }

            if (seconds == 0 && minutes == 0) {
                playSound();
                isBreak = true;
                minutes = breakLength;
                $('.type').html('Break');
                $('.timer').html(minutes + ':00');

            };

            if (seconds === 0) {
                seconds = 59;
                --minutes;
            }

            if (seconds < 10) {
                seconds = '0' + seconds;
            }

            $('.timer').html(minutes + ':' + seconds);
        }
    }, 1000);

}

How can I make this repeat itself?

pgtips
  • 1,198
  • 6
  • 22
  • 40
  • 1
    because i think there is a lot of benefit in working through this.. I'm just pointing you in the right direction for now. Let me know if you can't figure it out after going through the exercises in this link and I'll post some code. :-D https://www.codecademy.com/courses/javascript-lesson-205/0/1 – Chris L Nov 08 '15 at 02:36
  • and see here http://stackoverflow.com/questions/7065120/calling-a-javascript-function-recursively – Chris L Nov 08 '15 at 02:43
  • This isn't really an accurate way to make a timer because `setInterval` is only an approximation. You should record the start time and then on each interval tick, calculate the elapsed time since the start time. – Saad Nov 08 '15 at 03:03
  • 1
    Thanks @ChrisL, I will work through that. – pgtips Nov 08 '15 at 05:11
  • @saadq, this is just an exercise that is not mission critical but thanks for the info. – pgtips Nov 08 '15 at 05:12

2 Answers2

3

Define a new variable as a timeout id holder (let's call it resetTimeout) in your timer function scope:

var resetTimeout = null;

Add this additional code to the main function

var runFor = 60000; // 60000ms or 1 minute

Add logic in the main interval (first line):

if(runFor <= 0) {
    if(!resetTimeout) {
       // Create a reset timeout
       resetTimeout = setTimeout(function(){ runFor = 60000; resetTimeout = null; }, breakLength);
    }
    return;
}
else {
    runFor -= 1000; // Deduct time of this interval
}

This logic deducts 1000 ms or 1 second from runFor until it is fully consumed. Then creates a timeOut function that will reset it back to its original value and returns the current function until runFor is renewed. I used 60000 ms as an example and you can see the correct version in the full code below. Why do we assign the timeout to a variable? It is simple, we don't want to create more than one timeout. We'll set the timeout to null to allow recreation on the next interval.

Note that there are better ways of doing this but I decided to make as little modifications to your code as possible.

Here is the working code:

function timer(minutes, breakLength) {
    var seconds = 0;
    var originalMinutes = minutes;
    var resetTimeout = null;
    var totalRunFor = minutes * 60 * 1000; // Since minutes are independent
    var runFor = totalRunFor;

    timerId = setInterval(function() {
        if(runFor <= 0) {
            if(!resetTimeout) {
                
                // Create a reset timeout
                resetTimeout = setTimeout(function(){ runFor = totalRunFor; resetTimeout = null; }, breakLength);
            }
            return;
        }
        else {
            runFor -= 1000; // Deduct time of this interval
        }

        if (minutes >= 0) {

            if (seconds > 0) {
                --seconds;
            }

            if (seconds == 0 && minutes == 0) {
                //playSound();
                isBreak = true;
                minutes = originalMinutes;
                $('.type').html('Break');
                $('.timer').html(minutes + ':00');

            };

            if (seconds === 0) {
                seconds = 59;
                --minutes;
            }

            $('.timer').html(minutes + ':' + ((seconds < 10)?'0':'') + seconds);
        }
    }, 1000);

}
timer(1, 10000);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Run for a minute and stop for 10 seconds indefinitely.
<div class='type'></div>
<div class='timer'></div>
Schahriar SaffarShargh
  • 1,882
  • 2
  • 16
  • 24
  • 1
    setInterval is my favor JavaScript method because it is so dynamic in how it ties into time, literally. Cheers on the sound solution to the issue. – Alexander Dixon Nov 08 '15 at 03:21
  • Appreciate the response, thank you. I ended up splitting this into separate functions which made it easier for me to find the solution (needed the break timer to display as well, rather than running in the background). – pgtips Nov 08 '15 at 05:17
1

It may be conceptually easier to seperate this into multiple functions within the timer function

function timer(minutes, breakLength) {
    var ci = null;

    function ACountdown(minutes, callback) {
        var mm = minutes,
            ss = 0;
        ci = setInterval(function () {
            --ss;
            if (ss < 0)
                ss += 60, --mm;
            if (mm < 0) {
                // done
                clearInterval(ci);
                setTimeout(callback, 0);
            } else {
                $('.timer').html(mm + ':' + seconds);
            }
        }, 1e3);
    }

    function A() {
        // returned from break
        $('.type').html = 'Countdown';
        ACountdown(minutes, B);
    }
    function B() {
        // just finished countdown
        playSound();
        // going on break
        $('.type').html = 'Break';
        ACountdown(breakLength, A);
    }

    // set up any click handlers here, e.g.
    document.getElementById('cancel_button').addEventListener('click', function c() {
        this.removeEventListener('click', c);
        clearInterval(ci);
    });

    // start invocation chain
    A();
}
Paul S.
  • 58,277
  • 8
  • 106
  • 120
  • Thank you. I was able to solve it in a way I understand using this tip. It was definitely easier to split it into separate functions with callbacks. – pgtips Nov 08 '15 at 05:13