90

How do I pause and resume the setInterval() function using Javascript?

For example, maybe I have a stopwatch to tell you the number of seconds that you have been looking at the webpage. There is a 'Pause' and 'Resume' button. The reason why clearInterval() would not work here is because if the user clicks on the 'Pause' button at the 40th second and 800th millisecond, when he clicks on the 'Resume' button, the number of seconds elapsed must increase by 1 after 200 milliseconds. If I use the clearInterval() function on the timer variable (when the pause button is clicked) and then using the setInterval() function on the timer variable again (when the resume button is clicked), the number of seconds elapsed will increase by 1 only after 1000 milliseconds, which destroys the accuracy of the stopwatch.

So how do I do that?

isherwood
  • 58,414
  • 16
  • 114
  • 157
chris97ong
  • 6,870
  • 7
  • 32
  • 52
  • 1
    possible duplicate of [Code for a simple JavaScript countdown timer?](http://stackoverflow.com/questions/1191865/code-for-a-simple-javascript-countdown-timer) – HIRA THAKUR Jan 22 '14 at 08:39
  • 2
    possible duplicate of [How do I pause a windows.setInterval in javascript?](http://stackoverflow.com/questions/7279567/how-do-i-pause-a-windows-setinterval-in-javascript) – elclanrs Jan 22 '14 at 08:40
  • 1
    Take a look at [this answer](http://stackoverflow.com/a/7282347/1669279) from the remarcably similar question mentioned above. – Tibos Jan 22 '14 at 08:43

10 Answers10

147

You could use a flag to keep track of the status:

var output = $('h1');
var isPaused = false;
var time = 0;
var t = window.setInterval(function() {
  if(!isPaused) {
    time++;
    output.text("Seconds: " + time);
  }
}, 1000);

//with jquery
$('.pause').on('click', function(e) {
  e.preventDefault();
  isPaused = true;
});

$('.play').on('click', function(e) {
  e.preventDefault();
  isPaused = false;
});
h1 {
    font-family: Helvetica, Verdana, sans-serif;
    font-size: 12px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h1>Seconds: 0</h1>
<button class="play">Play</button>
<button class="pause">Pause</button>

This is just what I would do, I'm not sure if you can actually pause the setInterval.

Note: This system is easy and works pretty well for applications that don't require a high level of precision, but it won't consider the time elapsed in between ticks: if you click pause after half a second and later click play your time will be off by half a second.

Jonas Grumann
  • 10,438
  • 2
  • 22
  • 40
  • 1
    It will eventually `out run by the time.` if you have a number of `click` with `.pause` and `.play`, you cant be predict at which `n/1000` part will be `click` is made. Thus making a loss of `n/1000 Sec` or gain of `(1000-n)/1000 sec`. But in some cases that does not make sense, but in some breath taking scenarios they do a lot of harm. – vinrav Nov 26 '15 at 01:57
  • 1
    Anyway i liked the idea (simple and clean) and i am using it with my code in which there is no killing. `This comment is just to notify those who are going to kill some one with their script` – vinrav Nov 26 '15 at 02:08
  • 1
    Nice trick. Thanks for the answer but you did not PAUSE the setInterval function. – Ilyas karim Oct 19 '16 at 12:29
  • 3
    You cannot PAUSE the setInterval function, you can either STOP it (clearInterval), or let it run. The solution I provided was a solution for OP's problem, I did not claim it worked in every possible scenario. – Jonas Grumann Oct 19 '16 at 12:36
  • That didn't solve his problem. The results would still be probabilistic. A better choice would be to clear the interval, and increment to time what has passed since it's last fire. An interval event might increment it by one, and a pause event might increment it by 0.42. But you would also need to store the time each interval fired so you can compare against it. Date.now() is the function you want. – Jason Mitchell Nov 20 '19 at 16:30
17

You shouldn't measure time in interval function. Instead just save time when timer was started and measure difference when timer was stopped/paused. Use setInterval only to update displayed value. So there is no need to pause timer and you will get best possible accuracy in this way.

VitaliyG
  • 1,837
  • 1
  • 11
  • 11
  • 1
    Why should someone not measure time in an interval? If they wanted to display a live timer/countdown, then they would need to recalculate the elapsed duration every second anyways, why not track it there? – Hartley Brody Jul 20 '20 at 17:40
12

While @Jonas Giuro is right when saying that:

You cannot PAUSE the setInterval function, you can either STOP it (clearInterval), or let it run

On the other hand this behavior can be simulated with approach @VitaliyG suggested:

You shouldn't measure time in interval function. Instead just save time when timer was started and measure difference when timer was stopped/paused. Use setInterval only to update displayed value.

var output = $('h1');
var isPaused = false;
var time = new Date();
var offset = 0;
var t = window.setInterval(function() {
  if(!isPaused) {
    var milisec = offset + (new Date()).getTime() - time.getTime();
    output.text(parseInt(milisec / 1000) + "s " + (milisec % 1000));
  }
}, 10);

//with jquery
$('.toggle').on('click', function(e) {
  e.preventDefault();
  isPaused = !isPaused;
  if (isPaused) {
    offset += (new Date()).getTime() - time.getTime();
  } else {
    time = new Date();
  }

});
h1 {
    font-family: Helvetica, Verdana, sans-serif;
    font-size: 12px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h1>Seconds: 0</h1>
<button class="toggle">Toggle</button>
Community
  • 1
  • 1
sbgoran
  • 3,451
  • 2
  • 19
  • 30
8

Why not use a simpler approach? Add a class!

Simply add a class that tells the interval not to do anything. For example: on hover.

var i = 0;
this.setInterval(function() {
  if(!$('#counter').hasClass('pauseInterval')) { //only run if it hasn't got this class 'pauseInterval'
    console.log('Counting...');
    $('#counter').html(i++); //just for explaining and showing
  } else {
    console.log('Stopped counting');
  }
}, 500);

/* In this example, I'm adding a class on mouseover and remove it again on mouseleave. You can of course do pretty much whatever you like */
$('#counter').hover(function() { //mouse enter
    $(this).addClass('pauseInterval');
  },function() { //mouse leave
    $(this).removeClass('pauseInterval');
  }
);

/* Other example */
$('#pauseInterval').click(function() {
  $('#counter').toggleClass('pauseInterval');
});
body {
  background-color: #eee;
  font-family: Calibri, Arial, sans-serif;
}
#counter {
  width: 50%;
  background: #ddd;
  border: 2px solid #009afd;
  border-radius: 5px;
  padding: 5px;
  text-align: center;
  transition: .3s;
  margin: 0 auto;
}
#counter.pauseInterval {
  border-color: red;  
}
<!-- you'll need jQuery for this. If you really want a vanilla version, ask -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>


<p id="counter">&nbsp;</p>
<button id="pauseInterval">Pause</button></p>

I've been looking for this fast and easy approach for ages, so I'm posting several versions to introduce as many people to it as possible.

Aart den Braber
  • 864
  • 1
  • 11
  • 23
8

i wrote a simple ES6 class that may come handy. inspired by https://stackoverflow.com/a/58580918/4907364 answer

export class IntervalTimer {
    callbackStartTime;
    remaining = 0;
    paused = false;
    timerId = null;
    _callback;
    _delay;

    constructor(callback, delay) {
        this._callback = callback;
        this._delay = delay;
    }

    pause() {
        if (!this.paused) {
            this.clear();
            this.remaining = new Date().getTime() - this.callbackStartTime;
            this.paused = true;
        }
    }

    resume() {
        if (this.paused) {
            if (this.remaining) {
                setTimeout(() => {
                    this.run();
                    this.paused = false;
                    this.start();
                }, this.remaining);
            } else {
                this.paused = false;
                this.start();
            }
        }
    }

    clear() {
        clearInterval(this.timerId);
    }

    start() {
        this.clear();
        this.timerId = setInterval(() => {


            this.run();
        }, this._delay);
    }

    run() {
        this.callbackStartTime = new Date().getTime();
        this._callback();
    }
}

usage is pretty straightforward,

const interval = new IntervalTimer(console.log('aaa'), 3000);
interval.start();
interval.pause();
interval.resume();
interval.clear();
Anuj Raghuvanshi
  • 664
  • 1
  • 7
  • 24
omer
  • 2,435
  • 2
  • 24
  • 28
  • 1
    thank you for this but a quick note, there are private/public modifiers in your code that are from TypeScript and not part of ES6 – eballeste Aug 24 '21 at 07:11
5

My simple way:

function Timer (callback, delay) {
  let callbackStartTime
  let remaining = 0

  this.timerId = null
  this.paused = false

  this.pause = () => {
    this.clear()
    remaining -= Date.now() - callbackStartTime
    this.paused = true
  }
  this.resume = () => {
    window.setTimeout(this.setTimeout.bind(this), remaining)
    this.paused = false
  }
  this.setTimeout = () => {
    this.clear()
    this.timerId = window.setInterval(() => {
      callbackStartTime = Date.now()
      callback()
    }, delay)
  }
  this.clear = () => {
    window.clearInterval(this.timerId)
  }

  this.setTimeout()
}

How to use:

let seconds = 0
const timer = new Timer(() => {
  seconds++
  
  console.log('seconds', seconds)

  if (seconds === 8) {
    timer.clear()

    alert('Game over!')
  }
}, 1000)

timer.pause()
console.log('isPaused: ', timer.paused)

setTimeout(() => {
  timer.resume()
  console.log('isPaused: ', timer.paused)
}, 2500)


function Timer (callback, delay) {
  let callbackStartTime
  let remaining = 0

  this.timerId = null
  this.paused = false

  this.pause = () => {
    this.clear()
    remaining -= Date.now() - callbackStartTime
    this.paused = true
  }
  this.resume = () => {
    window.setTimeout(this.setTimeout.bind(this), remaining)
    this.paused = false
  }
  this.setTimeout = () => {
    this.clear()
    this.timerId = window.setInterval(() => {
      callbackStartTime = Date.now()
      callback()
    }, delay)
  }
  this.clear = () => {
    window.clearInterval(this.timerId)
  }

  this.setTimeout()
}

The code is written quickly and did not refactored, raise the rating of my answer if you want me to improve the code and give ES2015 version (classes).

4

I know this thread is old, but this could be another solution:

var do_this = null;

function y(){
   // what you wanna do
}

do_this = setInterval(y, 1000);

function y_start(){
    do_this = setInterval(y, 1000);
};
function y_stop(){
    do_this = clearInterval(do_this);
};
M1ke
  • 239
  • 2
  • 11
1

The following code, provides a precision way to pause resume a timer.

How it works:

When the timer is resumed after a pause, it generates a correction cycle using a single timeout, that will consider the pause offset (exact time when the timer was paused between cycles). After the correction cycle finishes, it schedules the following cycles with a regular setInteval, and continues normally the cycle execution.

This allows to pause/resume the timer, without losing the sync.

Code :

function Timer(_fn_callback_ , _timer_freq_){
    let RESUME_CORRECTION_RATE = 2;

    let _timer_statusCode_;
    let _timer_clockRef_;

    let _time_ellapsed_;        // will store the total time ellapsed
    let _time_pause_;           // stores the time when timer is paused
    let _time_lastCycle_;       // stores the time of the last cycle

    let _isCorrectionCycle_;
 
    /**
     * execute in each clock cycle
     */
    const nextCycle = function(){
        // calculate deltaTime
        let _time_delta_        = new Date() - _time_lastCycle_;
        _time_lastCycle_    = new Date();
        _time_ellapsed_   += _time_delta_;

        // if its a correction cicle (caused by a pause,
        // destroy the temporary timeout and generate a definitive interval
        if( _isCorrectionCycle_ ){
            clearTimeout( _timer_clockRef_ );
            clearInterval( _timer_clockRef_ );
            _timer_clockRef_    = setInterval(  nextCycle , _timer_freq_  );
            _isCorrectionCycle_ = false;
        }
        // execute callback
        _fn_callback_.apply( timer, [ timer ] );
    };

    // initialize timer
    _time_ellapsed_     = 0;
    _time_lastCycle_     = new Date();
    _timer_statusCode_   = 1;
    _timer_clockRef_     = setInterval(  nextCycle , _timer_freq_  );


    // timer public API
    const timer = {
        get statusCode(){ return _timer_statusCode_ },
        get timestamp(){
            let abstime;
            if( _timer_statusCode_=== 1 ) abstime = _time_ellapsed_ + ( new Date() - _time_lastCycle_ );
            else if( _timer_statusCode_=== 2 ) abstime = _time_ellapsed_ + ( _time_pause_ - _time_lastCycle_ );
            return abstime || 0;
        },

        pause : function(){
            if( _timer_statusCode_ !== 1 ) return this;
            // stop timers
            clearTimeout( _timer_clockRef_ );
            clearInterval( _timer_clockRef_ );
            // set new status and store current time, it will be used on
            // resume to calculate how much time is left for next cycle
            // to be triggered
            _timer_statusCode_ = 2;
            _time_pause_       = new Date();
            return this;
        },

        resume: function(){
            if( _timer_statusCode_ !== 2 ) return this;
            _timer_statusCode_  = 1;
            _isCorrectionCycle_ = true;
            const delayEllapsedTime = _time_pause_ - _time_lastCycle_;
            _time_lastCycle_    = new Date( new Date() - (_time_pause_ - _time_lastCycle_) );

            _timer_clockRef_ = setTimeout(  nextCycle , _timer_freq_ - delayEllapsedTime - RESUME_CORRECTION_RATE);

            return this;
        } 
    };
    return timer;
};


let myTimer = Timer( x=> console.log(x.timestamp), 1000);
<input type="button" onclick="myTimer.pause()" value="pause">
<input type="button" onclick="myTimer.resume()" value="resume">

Code source :

This Timer is a modified and simplified version of advanced-timer, a js library created by myself, with many more functionalities.

The full library and documentation is available in NPM and GITHUB

colxi
  • 7,640
  • 2
  • 45
  • 43
0

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

let timeCount = 0,
  currentTimeout;

function play() {
  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 pause() {
  clearInterval(currentTimeout);
}

function stop() {
  stopButton.hidden = true;
  pause();
  timeCount = 0;
  time.innerHTML = `00 : 00`;
}
<div>
  <h1 id="time">00 : 00</h1>
  <br />
  <div>
    <button onclick="play()">play</button>
    <button onclick="pause()">pause</button>
    <button onclick="stop()" id="stop" hidden>Reset</button>
  </div>
</div>
  • 1
    Welcome to Stackoverflow. Do not post an answer with merely codes. While your solution can be useful, you should also explain why the code will fix the problem that was described in the question and is more than 8 years old and has an accepted answer! – MD Zand Dec 02 '22 at 19:09
0

For those interested in an alternative (my edgecase for instance was the temporary pausing of an auto carousel function).

You can put the creation of the interval into a function and call onto it after sometime to restart it using setTimeout.

var carouselindex = 0,
    carouselinterval;

function changeoffset(dir) {
    // HTML Elements
    var container = document.getElementsByClassName("container")[0],
        indicator = document.getElementsByClassName("indicator")[0],
        width = container.offsetWidth,
        items = container.childElementCount;

    // Setting up index
    if (dir === '-' && carouselindex > 0) {
        carouselindex--;
    } else if (dir === '-' && carouselindex === 0) {
        carouselindex = (items - 1);
    } else if (dir === '+' && carouselindex < (items - 1)) {
        carouselindex++;
    } else if (dir === '+' && carouselindex === (items - 1)) {
        carouselindex = 0;
    }

    // Calculating offset
    var newoffset = Math.round(carouselindex * width),
        indicatoroffset = Math.round(carouselindex * 22);

    container.scrollTo(newoffset, 0);
    indicator.style.left = indicatoroffset + "px";
}

function startcarousel() {
    carouselinterval = setInterval(function() {
        changeoffset('+');
    }, 1000);
}

function pausecarousel(dir) {
    clearInterval(carouselinterval);
    changeoffset(dir);

    setTimeout(startcarousel, 5000);
}

startcarousel();

Some important notes to clear up any confusion. I use '+' or '-' to indicate the direction the carousel should move in and they are usually defined in the variable dir.

The only important part for people wondering about starting and pausing javascript Intervals is the code outside of the changeoffset function.