How can I pause setInterval() functions?
Asked Answered
A

10

93

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?

Ange answered 22/1, 2014 at 8:36 Comment(3)
possible duplicate of Code for a simple JavaScript countdown timer?Gonfalonier
possible duplicate of How do I pause a windows.setInterval in javascript?Agueweed
Take a look at this answer from the remarcably similar question mentioned above.Abdu
P
150

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.

Photomultiplier answered 22/1, 2014 at 8:42 Comment(5)
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.Bathilda
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 scriptBathilda
Nice trick. Thanks for the answer but you did not PAUSE the setInterval function.Fredfreda
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.Photomultiplier
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.Sweatbox
C
18

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.

Chare answered 22/1, 2014 at 8:45 Comment(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?Ezra
O
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>
Omland answered 30/3, 2017 at 23:19 Comment(0)
G
10

i wrote a simple ES6 class that may come handy. inspired by https://mcmap.net/q/224160/-how-can-i-pause-setinterval-functions 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();
Giraldo answered 12/4, 2020 at 16:29 Comment(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 ES6Avalanche
A
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.

Adelia answered 27/4, 2015 at 18:5 Comment(0)
G
5

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);
};
Girhiny answered 8/9, 2018 at 6:8 Comment(0)
F
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).

Floc answered 27/10, 2019 at 15:47 Comment(0)
H
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

Headmaster answered 8/10, 2018 at 6:48 Comment(0)
P
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>
Provo answered 30/11, 2022 at 0:40 Comment(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!Gounod
B
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.

Bayless answered 16/6, 2023 at 9:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.