【问题标题】:javascript: pause setTimeout();javascript: 暂停 setTimeout();
【发布时间】:2011-04-27 12:52:10
【问题描述】:

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

是否有暂停和恢复它?


有什么方法可以获取当前超时的剩余时间?
还是我必须在一个变量中,当设置超时时,存储当前时间,然后我们暂停,获取现在和那时之间的差异?

【问题讨论】:

  • 对于那些想知道的人来说,暂停是为了例如:一个 div 设置为在 5 秒内消失,在 3 秒时(剩下 2 秒)用户将鼠标悬停在 div 上,你暂停超时,一旦用户将鼠标从 div 上移开,您恢复它,2 秒后它就会消失。

标签: javascript timeout


【解决方案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 };
}

【讨论】:

【解决方案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 &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>

        【讨论】:

          【解决方案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>
                            

                            【讨论】:

                              猜你喜欢
                              • 1970-01-01
                              • 1970-01-01
                              • 2017-05-27
                              • 1970-01-01
                              • 1970-01-01
                              • 2011-02-07
                              • 2014-12-29
                              • 2018-02-15
                              • 1970-01-01
                              相关资源
                              最近更新 更多