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?
This question is related to
javascript
setinterval
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_x000D_
const timer = new Timer(() => {_x000D_
seconds++_x000D_
_x000D_
console.log('seconds', seconds)_x000D_
_x000D_
if (seconds === 8) {_x000D_
timer.clear()_x000D_
_x000D_
alert('Game over!')_x000D_
}_x000D_
}, 1000)_x000D_
_x000D_
timer.pause()_x000D_
console.log('isPaused: ', timer.paused)_x000D_
_x000D_
setTimeout(() => {_x000D_
timer.resume()_x000D_
console.log('isPaused: ', timer.paused)_x000D_
}, 2500)_x000D_
_x000D_
_x000D_
function Timer (callback, delay) {_x000D_
let callbackStartTime_x000D_
let remaining = 0_x000D_
_x000D_
this.timerId = null_x000D_
this.paused = false_x000D_
_x000D_
this.pause = () => {_x000D_
this.clear()_x000D_
remaining -= Date.now() - callbackStartTime_x000D_
this.paused = true_x000D_
}_x000D_
this.resume = () => {_x000D_
window.setTimeout(this.setTimeout.bind(this), remaining)_x000D_
this.paused = false_x000D_
}_x000D_
this.setTimeout = () => {_x000D_
this.clear()_x000D_
this.timerId = window.setInterval(() => {_x000D_
callbackStartTime = Date.now()_x000D_
callback()_x000D_
}, delay)_x000D_
}_x000D_
this.clear = () => {_x000D_
window.clearInterval(this.timerId)_x000D_
}_x000D_
_x000D_
this.setTimeout()_x000D_
}
_x000D_
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).
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);
};
i wrote a simple ES6 class that may come handy. inspired by https://stackoverflow.com/a/58580918/4907364 answer
export class IntervalTimer {
private callbackStartTime;
private remaining= 0;
private paused= false;
public timerId = null;
private readonly _callback;
private readonly _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);
}
private 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();
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.
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');_x000D_
var isPaused = false;_x000D_
var time = new Date();_x000D_
var offset = 0;_x000D_
var t = window.setInterval(function() {_x000D_
if(!isPaused) {_x000D_
var milisec = offset + (new Date()).getTime() - time.getTime();_x000D_
output.text(parseInt(milisec / 1000) + "s " + (milisec % 1000));_x000D_
}_x000D_
}, 10);_x000D_
_x000D_
//with jquery_x000D_
$('.toggle').on('click', function(e) {_x000D_
e.preventDefault();_x000D_
isPaused = !isPaused;_x000D_
if (isPaused) {_x000D_
offset += (new Date()).getTime() - time.getTime();_x000D_
} else {_x000D_
time = new Date();_x000D_
}_x000D_
_x000D_
});
_x000D_
h1 {_x000D_
font-family: Helvetica, Verdana, sans-serif;_x000D_
font-size: 12px;_x000D_
}
_x000D_
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>_x000D_
<h1>Seconds: 0</h1>_x000D_
<button class="toggle">Toggle</button>
_x000D_
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_){_x000D_
let RESUME_CORRECTION_RATE = 2;_x000D_
_x000D_
let _timer_statusCode_;_x000D_
let _timer_clockRef_;_x000D_
_x000D_
let _time_ellapsed_; // will store the total time ellapsed_x000D_
let _time_pause_; // stores the time when timer is paused_x000D_
let _time_lastCycle_; // stores the time of the last cycle_x000D_
_x000D_
let _isCorrectionCycle_;_x000D_
_x000D_
/**_x000D_
* execute in each clock cycle_x000D_
*/_x000D_
const nextCycle = function(){_x000D_
// calculate deltaTime_x000D_
let _time_delta_ = new Date() - _time_lastCycle_;_x000D_
_time_lastCycle_ = new Date();_x000D_
_time_ellapsed_ += _time_delta_;_x000D_
_x000D_
// if its a correction cicle (caused by a pause,_x000D_
// destroy the temporary timeout and generate a definitive interval_x000D_
if( _isCorrectionCycle_ ){_x000D_
clearTimeout( _timer_clockRef_ );_x000D_
clearInterval( _timer_clockRef_ );_x000D_
_timer_clockRef_ = setInterval( nextCycle , _timer_freq_ );_x000D_
_isCorrectionCycle_ = false;_x000D_
}_x000D_
// execute callback_x000D_
_fn_callback_.apply( timer, [ timer ] );_x000D_
};_x000D_
_x000D_
// initialize timer_x000D_
_time_ellapsed_ = 0;_x000D_
_time_lastCycle_ = new Date();_x000D_
_timer_statusCode_ = 1;_x000D_
_timer_clockRef_ = setInterval( nextCycle , _timer_freq_ );_x000D_
_x000D_
_x000D_
// timer public API_x000D_
const timer = {_x000D_
get statusCode(){ return _timer_statusCode_ },_x000D_
get timestamp(){_x000D_
let abstime;_x000D_
if( _timer_statusCode_=== 1 ) abstime = _time_ellapsed_ + ( new Date() - _time_lastCycle_ );_x000D_
else if( _timer_statusCode_=== 2 ) abstime = _time_ellapsed_ + ( _time_pause_ - _time_lastCycle_ );_x000D_
return abstime || 0;_x000D_
},_x000D_
_x000D_
pause : function(){_x000D_
if( _timer_statusCode_ !== 1 ) return this;_x000D_
// stop timers_x000D_
clearTimeout( _timer_clockRef_ );_x000D_
clearInterval( _timer_clockRef_ );_x000D_
// set new status and store current time, it will be used on_x000D_
// resume to calculate how much time is left for next cycle_x000D_
// to be triggered_x000D_
_timer_statusCode_ = 2;_x000D_
_time_pause_ = new Date();_x000D_
return this;_x000D_
},_x000D_
_x000D_
resume: function(){_x000D_
if( _timer_statusCode_ !== 2 ) return this;_x000D_
_timer_statusCode_ = 1;_x000D_
_isCorrectionCycle_ = true;_x000D_
const delayEllapsedTime = _time_pause_ - _time_lastCycle_;_x000D_
_time_lastCycle_ = new Date( new Date() - (_time_pause_ - _time_lastCycle_) );_x000D_
_x000D_
_timer_clockRef_ = setTimeout( nextCycle , _timer_freq_ - delayEllapsedTime - RESUME_CORRECTION_RATE);_x000D_
_x000D_
return this;_x000D_
} _x000D_
};_x000D_
return timer;_x000D_
};_x000D_
_x000D_
_x000D_
let myTimer = Timer( x=> console.log(x.timestamp), 1000);
_x000D_
<input type="button" onclick="myTimer.pause()" value="pause">_x000D_
<input type="button" onclick="myTimer.resume()" value="resume">
_x000D_
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
Simply add a class that tells the interval not to do anything. For example: on hover.
var i = 0;_x000D_
this.setInterval(function() {_x000D_
if(!$('#counter').hasClass('pauseInterval')) { //only run if it hasn't got this class 'pauseInterval'_x000D_
console.log('Counting...');_x000D_
$('#counter').html(i++); //just for explaining and showing_x000D_
} else {_x000D_
console.log('Stopped counting');_x000D_
}_x000D_
}, 500);_x000D_
_x000D_
/* 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 */_x000D_
$('#counter').hover(function() { //mouse enter_x000D_
$(this).addClass('pauseInterval');_x000D_
},function() { //mouse leave_x000D_
$(this).removeClass('pauseInterval');_x000D_
}_x000D_
);_x000D_
_x000D_
/* Other example */_x000D_
$('#pauseInterval').click(function() {_x000D_
$('#counter').toggleClass('pauseInterval');_x000D_
});
_x000D_
body {_x000D_
background-color: #eee;_x000D_
font-family: Calibri, Arial, sans-serif;_x000D_
}_x000D_
#counter {_x000D_
width: 50%;_x000D_
background: #ddd;_x000D_
border: 2px solid #009afd;_x000D_
border-radius: 5px;_x000D_
padding: 5px;_x000D_
text-align: center;_x000D_
transition: .3s;_x000D_
margin: 0 auto;_x000D_
}_x000D_
#counter.pauseInterval {_x000D_
border-color: red; _x000D_
}
_x000D_
<!-- you'll need jQuery for this. If you really want a vanilla version, ask -->_x000D_
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>_x000D_
_x000D_
_x000D_
<p id="counter"> </p>_x000D_
<button id="pauseInterval">Pause</button></p>
_x000D_
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.
Source: Stackoverflow.com