暂停 setTimeout() ;

如果我有一个通过 var t = setTimeout("dosomething()", 5000)设置的活动超时运行,

有没有可能暂停一下,然后继续呢?


是否有办法获得当前超时的剩余时间?
或者我必须在一个变量中,当设置超时时,存储当前时间,然后我们暂停,得到现在和当时之间的差异?

136819 次浏览

我不认为你会找到比 暂停更好的东西。无论如何,你总是可以安排另一次暂停,而不是“恢复”它。

不。你需要取消它(clearTimeout) ,测量时间,因为你启动它,并重新启动它与新的时间。

你可以查 ClearTimeout ()

或暂停取决于一个全局变量设置时,某个条件是命中。就像按了一个按钮。

  <button onclick="myBool = true" > pauseTimeout </button>


<script>
var myBool = false;


var t = setTimeout(function() {if (!mybool) {dosomething()}}, 5000);
</script>

“暂停”和“继续”在 setTimeout的语境中没有多大意义,因为 一次性的就是这样的。您可能希望暂停 setTimeout调用的 链条系列,在这种情况下不要安排下一个(也许可以取消通过 clearTimeout的未完成的调用,如下所示)。但是 setTimeout本身不循环,没有什么需要暂停和恢复。

如果你的意思是 setInterval然后没有,你不能暂停它,你只能取消它(clearInterval) ,然后重新安排它了。所有这些细节都在规范的 计时器部分中。

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

你可以像这样把 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();

这种东西应该有用。

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

如果你有几个 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>

您还可以使用事件来实现它。

你不需要计算时间差,而是开始和停止收听一个“嘀嗒”事件,这个事件一直在后台运行:

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


};

如果您正在使用 jquery,请查看 $. doTimeout插件。与 setTimeout 相比,这是一个巨大的改进,包括允许您使用您指定的一个字符串 id 来跟踪超时,并且不会在每次设置时发生变化,还可以实现简单的取消、轮询循环和去弹出等功能。我最常用的 jquery 插件之一。

不幸的是,它不支持即时暂停/恢复。为此,需要包装或扩展 $。DoTimeout,大概与接受的答案类似。

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

此示例将设置一个可暂停的 Timeout (pt _ hey) ,计划在两秒钟后发出“ hey”警报。另一个超时会在一秒后暂停 pt _ hey。第三个 Timeout 在两秒钟后恢复 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);

此示例设置一个可暂停的 Interval (pi _ hey) ,以便每两秒钟在控制台中写入“ hello world”。超时在5秒后暂停 pi _ hey。另一个超时在6秒后恢复 pi _ hey。因此 pi _ hey 将触发两次,运行一秒钟,暂停一秒钟,运行一秒钟,然后继续每2秒触发一次。

其他职能

  • ClearTimeout () 和 < em > clearInterval ()

    pt_hey.clearTimeout();pi_hey.clearInterval();是清除超时和间隔的简单方法。

  • GetTimeLeft ()

    pt_hey.getTimeLeft();pi_hey.getTimeLeft();将返回到下一次触发计划发生之前的多少毫秒。

一个稍微修改版本的蒂姆唐斯 回答。然而,因为我的编辑提姆 后退,我必须自己回答这个问题。我的解决方案可以使用额外的 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;

我需要能够暂停 setTimeout ()以获得类似幻灯片的特性。

下面是我自己的可暂停计时器的实现。它整合了 Tim Down 回答中的注释,比如更好的暂停(内核的注释)和一种原型形式(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 ()就像用 timer = new Timer( mycallback, 1000 )替换 timerID = setTimeout( mycallback, 1000)一样简单。然后 timer.pause()timer.resume()就可以使用了。

我需要计算运行时间和剩余时间,以显示一个进度条。使用公认的答案并不容易。对于此任务,“ 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();
});

复活

使用 Class-y 语法 Sugar 的 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 &amp; 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>

function delay (ms)   {  return new Promise(resolve => setTimeout(resolve, s));  }

“异步”工作演示: 网站 zarsoft.info

基于顶级答案的打字脚本实现

/** 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;
}
}


}

您可以像下面这样使 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)

如果有人想要尊敬的@SeanVieira 给你分享 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;
}
class pausable_timeout {
constructor(func, milliseconds) {
this.func = func;
this.date_ms = new Date().valueOf();
this.timeout = setTimeout(func, milliseconds);
this.time_left = milliseconds;
};


pause() {
clearTimeout(this.timeout);
const elapsed_time = new Date().valueOf() - this.date_ms;
this.time_left -= elapsed_time;
};


unpause() {
this.timeout = setTimeout(this.func, this.time_left);
this.date_ms = new Date().valueOf();
};
};


const timer = new pausable_timeout(() => /* your code */, 3000 /* your timeout in milliseconds */);
timer.pause();
timer.unpause();

程序很简单,我们将创建一个包含两个函数的类,pause函数和 unpause函数。

pause函数将清除 setTimeout并将从开始到现在的时间存储在 time_left变量中。unpause函数将通过将 time_left时间作为参数来重新创建 setTimeout

我在 TypeScript 中为滑块特性创建了以下代码:

class TimeoutSlider {
private callback: () => void;
private duration: number;
private timeReaming: number;
private startTime: number | null = null;
private timerId: NodeJS.Timeout | null = null;


constructor(callback: () => void, duration: number) {
this.callback = callback;
this.duration = duration;
this.timeReaming = duration;
}


public start() {
this.clear();
this.startTime = new Date().getTime();
this.timerId = setTimeout(this.callback, this.duration);
}


public pause() {
if (!this.startTime) {
throw new Error("Cannot pause a timer that has not been started");
}
this.clear();
this.timeReaming = this.duration - (new Date().getTime() - this.startTime);
}


public resume() {
this.clear();
this.startTime = new Date().getTime();
this.timerId = setTimeout(this.callback, this.timeReaming);
}


private clear() {
if (this.timerId) {
clearTimeout(this.timerId);
this.timerId = null;
}
}
}