How can I pause setInterval() functions?

JavascriptSetinterval

Javascript Problem Overview


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?

Javascript Solutions


Solution 1 - Javascript

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.

Solution 2 - Javascript

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.

Solution 3 - Javascript

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>

Solution 4 - Javascript

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.

Solution 5 - Javascript

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();

Solution 6 - Javascript

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).

Solution 7 - Javascript

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);
};

Solution 8 - Javascript

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionchris97ongView Question on Stackoverflow
Solution 1 - JavascriptJonas GrumannView Answer on Stackoverflow
Solution 2 - JavascriptVitaliyGView Answer on Stackoverflow
Solution 3 - JavascriptsbgoranView Answer on Stackoverflow
Solution 4 - JavascriptAart den BraberView Answer on Stackoverflow
Solution 5 - JavascriptomerView Answer on Stackoverflow
Solution 6 - JavascriptИлья ЗеленькоView Answer on Stackoverflow
Solution 7 - JavascriptM1keView Answer on Stackoverflow
Solution 8 - JavascriptcolxiView Answer on Stackoverflow