40
window.setInterval(function(){
  //do stuff
}, milisec);

Is there a way to stop this interval at will, and to resume it from where it lasted? Say, code runs every 5 sec. I stop it in the middle of the 2nd second, when resumed, I want it to run the remaining 3 seconds and continue to run afterwards every 5 sec. again.

George Irimiciuc
  • 4,573
  • 8
  • 44
  • 88
  • javascript is single threaded, `setInterval` statement just instructs the event loop to execute a function(first argument) after a fixed time in millis(second arg) and returns immediately. – Subin Sebastian Jul 13 '14 at 16:37
  • I was thinking of clearInterval(), but not sure exactly how to get remaining time, resume it from there and allow it to continue. – George Irimiciuc Jul 13 '14 at 16:40
  • 1
    Well store the current time when calling `setInterval`, and compare it to the then current time when your “stopping” of the whole process occurs. – CBroe Jul 13 '14 at 16:41
  • But how do I resume the interval to run for the remaining seconds, and then resume the interval to run for the initial timer? – George Irimiciuc Jul 13 '14 at 16:48
  • 2
    Have a look at [javascript: pause setTimeout();](http://stackoverflow.com/q/3969475/218196) ... it should be easy to adept it to `setInterval`. – Felix Kling Jul 13 '14 at 17:05

4 Answers4

51

Try this:

1- when you want to pause the timer, calculate the remaining milliseconds and store it somewhere then call clearInterval.

2- When you want to resume the timer, just make a call to setTimeout passing the remaining time stored in the previous step as the argument.

3- And in setTimeout's callback you should call setInterval again.

UPDATE: This is what you want, a changed version of javascript: pause setTimeout(); thanks to @Felix Kling

    function IntervalTimer(callback, interval) {
        var timerId, startTime, remaining = 0;
        var state = 0; //  0 = idle, 1 = running, 2 = paused, 3= resumed

        this.pause = function () {
            if (state != 1) return;

            remaining = interval - (new Date() - startTime);
            window.clearInterval(timerId);
            state = 2;
        };

        this.resume = function () {
            if (state != 2) return;

            state = 3;
            window.setTimeout(this.timeoutCallback, remaining);
        };

        this.timeoutCallback = function () {
            if (state != 3) return;

            callback();

            startTime = new Date();
            timerId = window.setInterval(callback, interval);
            state = 1;
        };

        startTime = new Date();
        timerId = window.setInterval(callback, interval);
        state = 1;
    }

Usage:

    var timer = new IntervalTimer(function () {
        alert("Done!");
    }, 5000);

    window.setTimeout(function () {
        timer.pause();
        window.setTimeout(function () {
            timer.resume();
        }, 5000);
    }, 2000);
Vahid
  • 6,639
  • 5
  • 37
  • 61
Alireza
  • 4,976
  • 1
  • 23
  • 36
6

To piggyback off Alireza's answer, here's an ES6 class that does the same thing with a bit more functionality, and doesn't start right away. You can set a maximum number of times the timer will fire off before automatically stopping, and pause and resume any number of times before the next time it's set to fire off.

export default class IntervalTimer{
  constructor(name, callback, interval, maxFires = null){
    this.remaining = 0;
    this.state = 0; //  0 = idle, 1 = running, 2 = paused, 3= resumed

    this.name = name;
    this.interval = interval; //in ms
    this.callback = callback;
    this.maxFires = maxFires;
    this.pausedTime = 0; //how long we've been paused for

    this.fires = 0;
  }

  proxyCallback(){
    if(this.maxFires != null && this.fires >= this.maxFires){
      this.stop();
      return;
    }
    this.lastTimeFired = new Date();
    this.fires++;
    this.callback();
  }

  start(){
    this.log.info('Starting Timer ' + this.name);
    this.timerId = setInterval(() => this.proxyCallback(), this.interval);
    this.lastTimeFired = new Date();
    this.state = 1;
    this.fires = 0;
  }

  pause(){
    if (this.state != 1 && this.state != 3) return;

    this.log.info('Pausing Timer ' + this.name);

    this.remaining = this.interval - (new Date() - this.lastTimeFired) + this.pausedTime;
    this.lastPauseTime = new Date();
    clearInterval(this.timerId);
    clearTimeout(this.resumeId);
    this.state = 2;
  }

  resume(){
    if (this.state != 2) return;

    this.pausedTime += new Date() - this.lastPauseTime;
    this.log.info(`Resuming Timer ${this.name} with ${this.remaining} remaining`);
    this.state = 3;
    this.resumeId = setTimeout(() => this.timeoutCallback(), this.remaining);
  }

  timeoutCallback(){
    if (this.state != 3) return;

    this.pausedTime = 0;
    this.proxyCallback();
    this.start();
  }

  stop(){
    if(this.state === 0) return;

    this.log.info('Stopping Timer %s. Fired %s/%s times', this.name, this.fires, this.maxFires);
    clearInterval(this.timerId);
    clearTimeout(this.resumeId);
    this.state = 0;
  }

  //set a new interval to use on the next interval loop
  setInterval(newInterval){
    this.log.info('Changing interval from %s to %s for %s', this.interval, newInterval, this.name);

    //if we're running do a little switch-er-oo
    if(this.state == 1){
      this.pause();
      this.interval = newInterval;
      this.resume();
    }
    //if we're already stopped, idle, or paused just switch it
    else{
      this.interval = newInterval;
    }
  }

  setMaxFires(newMax){
    if(newMax != null && this.fires >= newMax){
      this.stop();
    }
    this.maxFires = newMax;
  }
}
DShook
  • 14,833
  • 9
  • 45
  • 55
2

You should only need setTimeout with a go and stop - http://jsfiddle.net/devitate/QjdUR/1/

var cnt = 0;
var fivecnt = 0;
var go = false;

function timer() {
    if(!go)
        return;
    cnt++;
    if(cnt >= 5){
        cnt=0;
        everyFive();
    }
    jQuery("#counter").text(cnt);
    setTimeout(timer, 1000);
}

function everyFive(){
    fivecnt++;
    jQuery("#fiver").text(fivecnt);
}

function stopTimer(){
    go = false;  
} 
function startTimer(){
    go = true;
    timer();
}    
Dylan
  • 4,703
  • 1
  • 20
  • 23
0

let time = document.getElementById("time");
let stopButton = document.getElementById("stop");
let playButton = document.getElementById("play");

let timeCount = 0,
  currentTimeout;

function play_pause() {
  let status = playButton.innerHTML;
  if (status == "pause") {
    playButton.innerHTML = "Resume";
    clearInterval(currentTimeout);
    return;
  }
  playButton.innerHTML = "pause";
  stopButton.hidden = false;
  clearInterval(currentTimeout);
  currentTimeout = setInterval(() => {
    timeCount++;
    const min = String(Math.trunc(timeCount / 60)).padStart(2, 0);
    const sec = String(Math.trunc(timeCount % 60)).padStart(2, 0);
    time.innerHTML = `${min} : ${sec}`;
  }, 1000);
}

function reset() {
  stopButton.hidden = true;
  playButton.innerHTML = "play";
  clearInterval(currentTimeout);
  timeCount = 0;
  time.innerHTML = `00 : 00`;
}
<div>
  <h1 id="time">00 : 00</h1>
  <br />
  <div>
    <button onclick="play_pause()" id="play">play</button>
    <button onclick="reset()" id="stop" hidden>Reset</button>
  </div>
</div>
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Dec 02 '22 at 10:03