1

This a question that has been revolving in my head and today I had this idea to fix it, but it didn't work. What I basically want is to be able to pause and resume a timer, and when I resume it, it should continue in the time that I left it, for example: I stop it in the middle of the second it was in so when I resume it, half a second later it goes to the next second. This is my code.

$(document).ready(function() {
  a = new Timer("#timerText");
  a.set(12, 1);
  a.run();
});

function Timer(element) {

  var minutes, seconds, finalTimeInSeconds, displayMinutes, displaySeconds, interval = 1000, self = this, timeLeftToNextSecond = 1000;

  this.set = function(inputMinutes, inputSeconds) {

    finalTimeInSeconds = inputMinutes * 60 + inputSeconds;
    minutes = (Math.floor(finalTimeInSeconds / 60));
    seconds = finalTimeInSeconds % 60;

    this.print();
  }

  this.add = function(inputMinutes, inputSeconds) {

    finalTimeInSeconds += inputMinutes * 60 + inputSeconds;
    finalTimeInSeconds = (finalTimeInSeconds < 0) ? 0 : finalTimeInSeconds;
    minutes = (Math.floor(finalTimeInSeconds / 60));
    seconds = finalTimeInSeconds % 60;

    this.print();
  }

  this.subtract = function(inputMinutes, inputSeconds) {

    finalTimeInSeconds -= inputMinutes * 60 + inputSeconds;
    finalTimeInSeconds = (finalTimeInSeconds < 0) ? 0 : finalTimeInSeconds;
    minutes = (Math.floor(finalTimeInSeconds / 60));
    seconds = finalTimeInSeconds % 60;

    this.print();
  }

  this.reset = function() {

    this.set(0, 0);
  }

  this.print = function() {

    displayMinutes = (minutes.toString().length == 1) ? "0" + minutes : minutes; //ternary operator: adds a zero to the beggining 
    displaySeconds = (seconds.toString().length == 1) ? "0" + seconds : seconds; //of the number if it has only one caracter.

    $(element).text(displayMinutes + ":" + displaySeconds);
  }

  this.run = function() {

    ac = setInterval(function() {

      secondStarted = new Date;
      self.subtract(0, 1);
      interval = 1000;
    }, interval);
  }

  this.stop = function() {

    stopped = new Date;
    interval = stopped - secondStarted;
    clearInterval(ac);

  }

}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="timerText">00:00</div>

It does not work. If I stop it and then resume it the interval doesn't get back to 1000 after one cycle. Also, I don't think I subtracting the Dates the right way, because when I print the interval number the numbers look a little off. What am I doing wrong here?

Thanks if you answer.

cabralpinto
  • 1,814
  • 3
  • 13
  • 32

1 Answers1

1

The main problem is a that the setInterval function is called once, then if you change the value of interval variable, this does not affect the setInterval call.

A fast patch could be to use a setTimeout instead: called recursively.

  this.run = function() {
    var _f = function() {
      secondStarted = new Date;
      self.subtract(0, 1);
      interval = 1000;
      ac = setTimeout(_f,interval);
    }
    ac = setTimeout(_f, interval);
  }

and of course in stop function

clearTimeout(ac);

There is another issue. The interval var is not stopped-secondStarted but:

interval = 1000-(stopped - secondStarted);

This is how solve your specific problem with your approach. Look also to ac var is global and not in the scope.

If you want a more accurate timer you need to change approach because these function are inaccurate

$(document).ready(function() {
  a = new Timer("#timerText");
  a.set(12, 1);
  a.run();
   $('#btn').click(function() {
        if(a.running) {
            a.stop();
        }else {
            a.run();
        }
   });
});


function Timer(element) {

  var minutes, seconds, finalTimeInSeconds, displayMinutes, displaySeconds, interval = 1000, self = this, timeLeftToNextSecond = 1000;
  this.running = false;
  this.set = function(inputMinutes, inputSeconds) {

    finalTimeInSeconds = inputMinutes * 60 + inputSeconds;
    minutes = (Math.floor(finalTimeInSeconds / 60));
    seconds = finalTimeInSeconds % 60;

    this.print();
  }

  this.add = function(inputMinutes, inputSeconds) {

    finalTimeInSeconds += inputMinutes * 60 + inputSeconds;
    finalTimeInSeconds = (finalTimeInSeconds < 0) ? 0 : finalTimeInSeconds;
    minutes = (Math.floor(finalTimeInSeconds / 60));
    seconds = finalTimeInSeconds % 60;

    this.print();
  }

  this.subtract = function(inputMinutes, inputSeconds) {

    finalTimeInSeconds -= inputMinutes * 60 + inputSeconds;
    finalTimeInSeconds = (finalTimeInSeconds < 0) ? 0 : finalTimeInSeconds;
    minutes = (Math.floor(finalTimeInSeconds / 60));
    seconds = finalTimeInSeconds % 60;

    this.print();
  }

  this.reset = function() {

    this.set(0, 0);
  }

  this.print = function() {

    displayMinutes = (minutes.toString().length == 1) ? "0" + minutes : minutes; //ternary operator: adds a zero to the beggining 
    displaySeconds = (seconds.toString().length == 1) ? "0" + seconds : seconds; //of the number if it has only one caracter.

    $(element).text(displayMinutes + ":" + displaySeconds);
  }

  this.run = function() {
    this.running = !false;
    var _f = function() {
      secondStarted = new Date;
      self.subtract(0, 1);
      interval = 1000;
      ac = setTimeout(_f,interval);
    }
    ac = setTimeout(_f, interval);
  }
  this.stop = function() {
    this.running = false;
    stopped = new Date;
    interval = 1000 -(stopped - secondStarted);
    clearTimeout(ac);

  }

}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="timerText">00:00</div>
<button type="button" id="btn">Toggle Start</button>
Community
  • 1
  • 1
Luca Rainone
  • 16,138
  • 2
  • 38
  • 52
  • You sir, are a very good person :-) This solved my problem. For right now, I don't need it to be extremely accurate, but if I find your second solution simple enough I'll use it. Also, your second function is accurate, right? Because you are sill using `setTimeout`. – cabralpinto Sep 15 '15 at 11:37
  • One more question, why do you do `this.running` instead of putting running just next to the other variables? – cabralpinto Sep 15 '15 at 11:38
  • I'm not looked your code in deep. I've built a working example with toggle button :-) So the `running` flag simply allow me to see the state of timer. Maybe there was a better option. `setTimeout` and `setInterval` are both inaccurate. – Luca Rainone Sep 15 '15 at 11:53