如何暂停 setInterval() 函数?
Posted
技术标签:
【中文标题】如何暂停 setInterval() 函数?【英文标题】:How can I pause setInterval() functions? 【发布时间】:2014-02-12 05:08:18 【问题描述】:如何使用 javascript 暂停和恢复 setInterval() 函数?
例如,也许我有一个秒表来告诉您您浏览网页的秒数。有一个“暂停”和“恢复”按钮。 clearInterval() 在这里不起作用的原因是,如果用户在第 40 秒和第 800 毫秒点击“暂停”按钮,当他点击“恢复”按钮时,经过的秒数必须在 200 毫秒后增加 1。如果我在计时器变量上使用 clearInterval() 函数(单击暂停按钮时),然后再次在计时器变量上使用 setInterval() 函数(单击恢复按钮时),经过的秒数将增加1 仅在 1000 毫秒之后,这会破坏秒表的准确性。
那我该怎么做呢?
【问题讨论】:
Code for a simple JavaScript countdown timer? 的可能重复项 How do I pause a windows.setInterval in javascript?的可能重复 看看上面提到的非常相似的问题中的this answer。 【参考方案1】:您可以使用标志来跟踪状态:
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>
这正是我会做的,我不确定你是否真的可以暂停 setInterval。
注意:这个系统很简单,非常适合不需要高精度的应用程序,但它不会考虑滴答之间经过的时间:如果你在半秒后点击暂停,然后再点击玩你的时间会少半秒。
【讨论】:
如果你有多个click
和.pause
和.play
,它最终会out run by the time.
,你无法预测n/1000
部分将是click
。因此损失n/1000 Sec
或获得(1000-n)/1000 sec
。但在某些情况下,这没有意义,但在某些令人叹为观止的情况下,它们会造成很大的伤害。
无论如何我喜欢这个想法(简单而干净),我将它与我的代码一起使用,其中没有杀戮。 This comment is just to notify those who are going to kill some one with their script
不错的把戏。感谢您的回答,但您没有暂停 setInterval 函数。
你不能暂停 setInterval 函数,你可以停止它(clearInterval),或者让它运行。我提供的解决方案是针对 OP 问题的解决方案,我并没有声称它适用于所有可能的情况。
这并没有解决他的问题。结果仍然是概率性的。更好的选择是清除间隔,并增加自上次火灾以来经过的时间。间隔事件可能会增加 1,暂停事件可能会增加 0.42。但是您还需要存储每个间隔触发的时间,以便与它进行比较。 Date.now() 是你想要的函数。【参考方案2】:
您不应该在区间函数中测量时间。相反,只需在计时器启动时节省时间,并在计时器停止/暂停时测量差异。仅使用 setInterval 更新显示的值。因此无需暂停计时器,您将通过这种方式获得尽可能高的准确性。
【讨论】:
为什么人们不应该在间隔中测量时间?如果他们想显示实时计时器/倒计时,那么无论如何他们都需要重新计算经过的持续时间,为什么不在那里跟踪呢?【参考方案3】:虽然@Jonas Giuro 说得对:
你不能暂停 setInterval 函数,你可以停止它(clearInterval),或者让它运行
另一方面,这种行为可以用approach @VitaliyG suggested模拟:
您不应该在区间函数中测量时间。相反,只需在计时器启动时节省时间,并在计时器停止/暂停时测量差异。仅使用 setInterval 更新显示值。
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>
【讨论】:
【参考方案4】:为什么不使用更简单的方法呢?添加课程!
只需添加一个告诉间隔不要做任何事情的类。例如:悬停时。
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"> </p>
<button id="pauseInterval">Pause</button></p>
多年来,我一直在寻找这种快速简便的方法,因此我发布了几个版本,以向尽可能多的人介绍它。
【讨论】:
【参考方案5】:我的简单方法:
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()
使用方法:
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()
代码写得很快,没有重构,如果你想让我改进代码并给出ES2015版本(类),请提高我的回答评分。
【讨论】:
【参考方案6】:我编写了一个简单的 ES6 类,它可能会派上用场。 灵感来自https://***.com/a/58580918/4907364答案
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();
用法很简单,
const interval = new IntervalTimer(console.log('aaa'), 3000);
interval.start();
interval.pause();
interval.resume();
interval.clear();
【讨论】:
谢谢您,但请注意,您的代码中有私有/公共修饰符来自 TypeScript,而不是 ES6 的一部分【参考方案7】:我知道这个帖子很旧,但这可能是另一种解决方案:
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);
;
【讨论】:
【参考方案8】:以下代码提供了一种暂停恢复计时器的精确方法。
工作原理:
当定时器暂停后恢复时,它会使用单个timeout
生成一个更正周期,这将考虑暂停偏移量(定时器时的确切时间)在循环之间暂停)。修正周期结束后,它会以常规的setInteval
调度后续周期,并继续正常执行周期。
这允许暂停/恢复计时器,而不会丢失同步。
代码:
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">
代码来源:
这个Timer是我自己创建的js库advanced-timer
的修改简化版,功能更多。
完整的库和文档在NPM 和GITHUB 中提供
【讨论】:
以上是关于如何暂停 setInterval() 函数?的主要内容,如果未能解决你的问题,请参考以下文章
在 React Native Android 和 iOS 中使用 Set Interval
使用setInterval通过JQuery暂停幻灯片放映吗?
jquery html JS setInterval暂停和重启
地球上最准确的视频暂停(currentTime Vs mediaTime 和 requestVideoFrameCallback Vs setInterval)