javascript: 暂停 setTimeout();
Posted
技术标签:
【中文标题】javascript: 暂停 setTimeout();【英文标题】:javascript: pause setTimeout(); 【发布时间】:2011-04-27 12:52:10 【问题描述】:如果我有一个通过var t = setTimeout("dosomething()", 5000)
设置的活动超时运行,
是否有暂停和恢复它?
有什么方法可以获取当前超时的剩余时间? 还是我必须在一个变量中,当设置超时时,存储当前时间,然后我们暂停,获取现在和那时之间的差异?
【问题讨论】:
对于那些想知道的人来说,暂停是为了例如:一个 div 设置为在 5 秒内消失,在 3 秒时(剩下 2 秒)用户将鼠标悬停在 div 上,你暂停超时,一旦用户将鼠标从 div 上移开,您恢复它,2 秒后它就会消失。 【参考方案1】:您可以像这样包装window.setTimeout
,我认为这与您在问题中的建议相似:
var Timer = function(callback, delay)
var timerId, start, remaining = delay;
this.pause = function()
window.clearTimeout(timerId);
timerId = null;
remaining -= Date.now() - start;
;
this.resume = function()
if (timerId)
return;
start = Date.now();
timerId = window.setTimeout(callback, remaining);
;
this.resume();
;
var timer = new Timer(function()
alert("Done!");
, 1000);
timer.pause();
// Do some stuff...
timer.resume();
【讨论】:
@yckart:回滚,抱歉。这是一个很好的补充,只是在 Internet Explorer setTimeout() 添加附加参数。 如果你这样做timer.resume(); timer.resume();
,你最终会同时出现两个超时。这就是为什么你想要么先clearTimeout(timerId)
,要么在简历一开始就短路if (timerId) return;
。
嘿,我喜欢这个答案,但我不得不将:var timerId, start, remaining;
拉到 Class 范围之外,然后在里面添加 remaining = delay;
以捕获参数。像魅力一样工作!
@Josh979:你真的不需要这样做,这样做是个坏主意,因为它暴露了应该是内部的变量。也许您已将代码粘贴到块中(例如,在 if (blah) ...
中)或其他内容?
由于某种原因,初始化后我只运行一次。【参考方案2】:
这样的事情应该可以解决问题。
function Timer(fn, countdown)
var ident, complete = false;
function _time_diff(date1, date2)
return date2 ? date2 - date1 : new Date().getTime() - date1;
function cancel()
clearTimeout(ident);
function pause()
clearTimeout(ident);
total_time_run = _time_diff(start_time);
complete = total_time_run >= countdown;
function resume()
ident = complete ? -1 : setTimeout(fn, countdown - total_time_run);
var start_time = new Date().getTime();
ident = setTimeout(fn, countdown);
return cancel: cancel, pause: pause, resume: resume ;
【讨论】:
我将+new Date()
更改为new Date().getTime()
,因为它更快:jsperf.com/date-vs-gettime
最好的,爸爸!【参考方案3】:
没有。您需要取消它 (clearTimeout
),测量自启动以来的时间,然后使用新时间重新启动它。
【讨论】:
【参考方案4】:Tim Downs answer 的略微修改版本。但是,由于 Tim rolled back 我的编辑,我必须自己回答这个问题。我的解决方案可以使用额外的 arguments
作为第三个 (3, 4, 5...) 参数并清除计时器:
function Timer(callback, delay)
var args = arguments,
self = this,
timer, start;
this.clear = function ()
clearTimeout(timer);
;
this.pause = function ()
this.clear();
delay -= new Date() - start;
;
this.resume = function ()
start = new Date();
timer = setTimeout(function ()
callback.apply(self, Array.prototype.slice.call(args, 2, args.length));
, delay);
;
this.resume();
正如 Tim 所提到的,IE lt 9
中没有额外的参数,但是我进行了一些工作,以便它也可以在 oldIE
中使用。
用法:new Timer(Function, Number, arg1, arg2, arg3...)
function callback(foo, bar)
console.log(foo); // "foo"
console.log(bar); // "bar"
var timer = new Timer(callback, 1000, "foo", "bar");
timer.pause();
document.onclick = timer.resume;
【讨论】:
【参考方案5】:“暂停”和“恢复”在setTimeout
的上下文中没有多大意义,这是一个一次性的事情。您可能希望暂停setTimeout
调用的链式系列,在这种情况下,不要安排下一个调用(也许通过clearTimeout
取消未完成的调用,如下所示)。但是setTimeout
本身不会循环,没有什么可以暂停和恢复的。
如果你的意思是setInterval
那么不行,你不能暂停它,你只能取消它(clearInterval
)然后重新安排它。所有这些的详细信息都在规范的Timers section 中。
// Setting
var t = setInterval(doSomething, 1000);
// Pausing (which is really stopping)
clearInterval(t);
t = 0;
// Resuming (which is really just setting again)
t = setInterval(doSomething, 1000);
【讨论】:
【参考方案6】:Timeout 很容易找到解决方案,但 Interval 有点棘手。
我想出了以下两个类来解决这个问题:
function PauseableTimeout(func, delay)
this.func = func;
var _now = new Date().getTime();
this.triggerTime = _now + delay;
this.t = window.setTimeout(this.func,delay);
this.paused_timeLeft = 0;
this.getTimeLeft = function()
var now = new Date();
return this.triggerTime - now;
this.pause = function()
this.paused_timeLeft = this.getTimeLeft();
window.clearTimeout(this.t);
this.t = null;
this.resume = function()
if (this.t == null)
this.t = window.setTimeout(this.func, this.paused_timeLeft);
this.clearTimeout = function() window.clearTimeout(this.t);
function PauseableInterval(func, delay)
this.func = func;
this.delay = delay;
this.triggerSetAt = new Date().getTime();
this.triggerTime = this.triggerSetAt + this.delay;
this.i = window.setInterval(this.func, this.delay);
this.t_restart = null;
this.paused_timeLeft = 0;
this.getTimeLeft = function()
var now = new Date();
return this.delay - ((now - this.triggerSetAt) % this.delay);
this.pause = function()
this.paused_timeLeft = this.getTimeLeft();
window.clearInterval(this.i);
this.i = null;
this.restart = function(sender)
sender.i = window.setInterval(sender.func, sender.delay);
this.resume = function()
if (this.i == null)
this.i = window.setTimeout(this.restart, this.paused_timeLeft, this);
this.clearInterval = function() window.clearInterval(this.i);
这些可以这样实现:
var pt_hey = new PauseableTimeout(function()
alert("hello");
, 2000);
window.setTimeout(function()
pt_hey.pause();
, 1000);
window.setTimeout("pt_hey.start()", 2000);
此示例将设置一个可暂停的超时 (pt_hey),它计划在两秒后发出“嘿”警报。另一个超时在一秒钟后暂停 pt_hey。第三个超时在两秒后恢复 pt_hey。 pt_hey 运行一秒钟,暂停一秒钟,然后恢复运行。 pt_hey 三秒后触发。
现在是更棘手的间隔
var pi_hey = new PauseableInterval(function()
console.log("hello world");
, 2000);
window.setTimeout("pi_hey.pause()", 5000);
window.setTimeout("pi_hey.resume()", 6000);
这个例子设置了一个可暂停的间隔(pi_hey),每两秒在控制台中写入“hello world”。五秒后超时暂停 pi_hey。另一个超时将在 6 秒后恢复 pi_hey。所以pi_hey会触发两次,运行一秒,暂停一秒,运行一秒,然后每2秒继续触发一次。
其他功能
clearTimeout() 和 clearInterval()
pt_hey.clearTimeout();
和 pi_hey.clearInterval();
是清除超时和间隔的简单方法。
getTimeLeft()
pt_hey.getTimeLeft();
和 pi_hey.getTimeLeft();
将返回多少毫秒,直到安排下一个触发器发生。
【讨论】:
你能解释一下你的想法吗,为什么我们需要一个复杂的Class来暂停setInterval
?我认为一个简单的if(!true) return;
就可以解决问题,还是我错了?
我这样做是为了让您可以从字面上暂停间隔,而不是在触发时跳过呼叫。如果在游戏中,每 60 秒释放一次加电,而我在即将触发之前暂停游戏,使用您的方法,我将不得不再等待一分钟才能再次加电。这并不是真正的暂停,这只是忽略了一个呼叫。相反,我的方法实际上是暂停,因此,就游戏而言,加电是“准时”释放的。【参考方案7】:
/复活
使用 Class-y 语法糖的 ES6 版本?
(稍作修改:添加了 start())
class Timer
constructor(callback, delay)
this.callback = callback
this.remainingTime = delay
this.startTime
this.timerId
pause()
clearTimeout(this.timerId)
this.remainingTime -= new Date() - this.startTime
resume()
this.startTime = new Date()
clearTimeout(this.timerId)
this.timerId = setTimeout(this.callback, this.remainingTime)
start()
this.timerId = setTimeout(this.callback, this.remainingTime)
// supporting code
const pauseButton = document.getElementById('timer-pause')
const resumeButton = document.getElementById('timer-resume')
const startButton = document.getElementById('timer-start')
const timer = new Timer(() =>
console.log('called');
document.getElementById('change-me').classList.add('wow')
, 3000)
pauseButton.addEventListener('click', timer.pause.bind(timer))
resumeButton.addEventListener('click', timer.resume.bind(timer))
startButton.addEventListener('click', timer.start.bind(timer))
<!doctype html>
<html>
<head>
<title>Traditional HTML Document. ZZz...</title>
<style type="text/css">
.wow color: blue; font-family: Tahoma, sans-serif; font-size: 1em;
</style>
</head>
<body>
<h1>DOM & javascript</h1>
<div id="change-me">I'm going to repaint my life, wait and see.</div>
<button id="timer-start">Start!</button>
<button id="timer-pause">Pause!</button>
<button id="timer-resume">Resume!</button>
</body>
</html>
【讨论】:
【参考方案8】:我需要计算已用时间和剩余时间以显示进度条。使用公认的答案并不容易。对于这个任务,'setInterval' 比 'setTimeout' 好。所以,我创建了这个可以在任何项目中使用的 Timer 类。
https://jsfiddle.net/ashraffayad/t0mmv853/
'use strict';
//Constructor
var Timer = function(cb, delay)
this.cb = cb;
this.delay = delay;
this.elapsed = 0;
this.remaining = this.delay - self.elapsed;
;
console.log(Timer);
Timer.prototype = function()
var _start = function(x, y)
var self = this;
if (self.elapsed < self.delay)
clearInterval(self.interval);
self.interval = setInterval(function()
self.elapsed += 50;
self.remaining = self.delay - self.elapsed;
console.log('elapsed: ' + self.elapsed,
'remaining: ' + self.remaining,
'delay: ' + self.delay);
if (self.elapsed >= self.delay)
clearInterval(self.interval);
self.cb();
, 50);
,
_pause = function()
var self = this;
clearInterval(self.interval);
,
_restart = function()
var self = this;
self.elapsed = 0;
console.log(self);
clearInterval(self.interval);
self.start();
;
//public member definitions
return
start: _start,
pause: _pause,
restart: _restart
;
();
// - - - - - - - - how to use this class
var restartBtn = document.getElementById('restart');
var pauseBtn = document.getElementById('pause');
var startBtn = document.getElementById('start');
var timer = new Timer(function()
console.log('Done!');
, 2000);
restartBtn.addEventListener('click', function(e)
timer.restart();
);
pauseBtn.addEventListener('click', function(e)
timer.pause();
);
startBtn.addEventListener('click', function(e)
timer.start();
);
【讨论】:
【参考方案9】:基于评分最高的答案的打字稿实现
/** Represents the `setTimeout` with an ability to perform pause/resume actions */
export class Timer
private _start: Date;
private _remaining: number;
private _durationTimeoutId?: NodeJS.Timeout;
private _callback: (...args: any[]) => void;
private _done = false;
get done ()
return this._done;
constructor(callback: (...args: any[]) => void, ms = 0)
this._callback = () =>
callback();
this._done = true;
;
this._remaining = ms;
this.resume();
/** pauses the timer */
pause(): Timer
if (this._durationTimeoutId && !this._done)
this._clearTimeoutRef();
this._remaining -= new Date().getTime() - this._start.getTime();
return this;
/** resumes the timer */
resume(): Timer
if (!this._durationTimeoutId && !this._done)
this._start = new Date;
this._durationTimeoutId = setTimeout(this._callback, this._remaining);
return this;
/**
* clears the timeout and marks it as done.
*
* After called, the timeout will not resume
*/
clearTimeout()
this._clearTimeoutRef();
this._done = true;
private _clearTimeoutRef()
if (this._durationTimeoutId)
clearTimeout(this._durationTimeoutId);
this._durationTimeoutId = undefined;
【讨论】:
【参考方案10】:您可以查看clearTimeout()
或暂停取决于在满足特定条件时设置的全局变量。就像按下了一个按钮。
<button onclick="myBool = true" > pauseTimeout </button>
<script>
var myBool = false;
var t = setTimeout(function() if (!mybool) dosomething(), 5000);
</script>
【讨论】:
【参考方案11】:你也可以用事件来实现它。
您无需计算时间差,而是开始和停止收听在后台持续运行的“tick”事件:
var Slideshow =
_create: function()
this.timer = window.setInterval(function()
$(window).trigger('timer:tick'); , 8000);
,
play: function()
$(window).bind('timer:tick', function()
// stuff
);
,
pause: function()
$(window).unbind('timer:tick');
;
【讨论】:
【参考方案12】:如果您仍然使用 jquery,请查看 $.doTimeout 插件。这件事是对 setTimeout 的巨大改进,包括让您使用您指定的单个字符串 id 跟踪您的超时,并且每次设置它都不会改变,并实现简单的取消、轮询循环和去抖动,以及更多的。我最常用的 jquery 插件之一。
不幸的是,它不支持开箱即用的暂停/恢复。为此,您需要包装或扩展 $.doTimeout,大概类似于接受的答案。
【讨论】:
我希望 doTimeout 会暂停/恢复,但在查看完整文档、循环示例甚至源代码时我没有看到它。我能看到的最接近暂停的是取消,但是我必须再次使用函数重新创建计时器。我错过了什么吗? 抱歉让你走错了路。我已经从我的回答中消除了这种不准确之处。【参考方案13】:我需要能够暂停 setTimeout() 以获得类似幻灯片的功能。
这是我自己实现的可暂停计时器。它集成了在 Tim Down 的回答中看到的 cmets,例如更好的暂停(内核的评论)和一种原型设计形式(Umur Gedik 的评论)。
function Timer( callback, delay )
/** Get access to this object by value **/
var self = this;
/********************* PROPERTIES *********************/
this.delay = delay;
this.callback = callback;
this.starttime;// = ;
this.timerID = null;
/********************* METHODS *********************/
/**
* Pause
*/
this.pause = function()
/** If the timer has already been paused, return **/
if ( self.timerID == null )
console.log( 'Timer has been paused already.' );
return;
/** Pause the timer **/
window.clearTimeout( self.timerID );
self.timerID = null; // this is how we keep track of the timer having beem cleared
/** Calculate the new delay for when we'll resume **/
self.delay = self.starttime + self.delay - new Date().getTime();
console.log( 'Paused the timer. Time left:', self.delay );
/**
* Resume
*/
this.resume = function()
self.starttime = new Date().getTime();
self.timerID = window.setTimeout( self.callback, self.delay );
console.log( 'Resuming the timer. Time left:', self.delay );
/********************* CONSTRUCTOR METHOD *********************/
/**
* Private constructor
* Not a language construct.
* Mind var to keep the function private and () to execute it right away.
*/
var __construct = function()
self.starttime = new Date().getTime();
self.timerID = window.setTimeout( self.callback, self.delay )
(); /* END __construct */
/* END Timer */
例子:
var timer = new Timer( function() console.log( 'hey! this is a timer!' ); , 10000 );
timer.pause();
要测试代码,请使用timer.resume()
和timer.pause()
几次,然后检查还剩多少时间。 (确保您的控制台已打开。)
使用此对象代替 setTimeout() 就像将 timerID = setTimeout( mycallback, 1000)
替换为 timer = new Timer( mycallback, 1000 )
一样简单。然后timer.pause()
和timer.resume()
可供您使用。
【讨论】:
【参考方案14】:function delay (ms) return new Promise(resolve => setTimeout(resolve, s));
“异步”工作演示在: site zarsoft.info
【讨论】:
【参考方案15】:您可以执行以下操作以使 setTimeout 在服务器端 (Node.js) 上可暂停
const PauseableTimeout = function(callback, delay)
var timerId, start, remaining = delay;
this.pause = function()
global.clearTimeout(timerId);
remaining -= Date.now() - start;
;
this.resume = function()
start = Date.now();
global.clearTimeout(timerId);
timerId = global.setTimeout(callback, remaining);
;
this.resume();
;
你可以检查如下
var timer = new PauseableTimeout(function()
console.log("Done!");
, 3000);
setTimeout(()=>
timer.pause();
console.log("setTimeout paused");
,1000);
setTimeout(()=>
console.log("setTimeout time complete");
,3000)
setTimeout(()=>
timer.resume();
console.log("setTimeout resume again");
,5000)
【讨论】:
【参考方案16】:如果有人想要尊敬的@SeanVieira here 共享的 TypeScript 版本,你可以使用这个:
public timer(fn: (...args: any[]) => void, countdown: number): onCancel: () => void, onPause: () => void, onResume: () => void
let ident: NodeJS.Timeout | number;
let complete = false;
let totalTimeRun: number;
const onTimeDiff = (date1: number, date2: number) =>
return date2 ? date2 - date1 : new Date().getTime() - date1;
;
const handlers =
onCancel: () =>
clearTimeout(ident as NodeJS.Timeout);
,
onPause: () =>
clearTimeout(ident as NodeJS.Timeout);
totalTimeRun = onTimeDiff(startTime, null);
complete = totalTimeRun >= countdown;
,
onResume: () =>
ident = complete ? -1 : setTimeout(fn, countdown - totalTimeRun);
;
const startTime = new Date().getTime();
ident = setTimeout(fn, countdown);
return handlers;
【讨论】:
【参考方案17】:我认为您找不到比 clearTimeout 更好的东西了。无论如何,您可以稍后再安排一次超时,而不是“恢复”它。
【讨论】:
【参考方案18】:如果您要隐藏多个 div,您可以使用 setInterval
和多个循环来执行以下操作:
<div id="div1">1</div><div id="div2">2</div>
<div id="div3">3</div><div id="div4">4</div>
<script>
function hideDiv(elm)
var interval,
unit = 1000,
cycle = 5,
hide = function()
interval = setInterval(function()
if(--cycle === 0)
elm.style.display = 'none';
clearInterval(interval);
elm.setAttribute('data-cycle', cycle);
elm.innerHTML += '*';
, unit);
;
elm.onmouseover = function()
clearInterval(interval);
;
elm.onmouseout = function()
hide();
;
hide();
function hideDivs(ids)
var id;
while(id = ids.pop())
hideDiv(document.getElementById(id));
hideDivs(['div1','div2','div3','div4']);
</script>
【讨论】:
以上是关于javascript: 暂停 setTimeout();的主要内容,如果未能解决你的问题,请参考以下文章
javascript setTimeout 和递归函数执行流程