【问题标题】:How can I pause setInterval() functions?如何暂停 setInterval() 函数?
【发布时间】:2014-02-12 05:08:18
【问题描述】:

如何使用 Javascript 暂停和恢复 setInterval() 函数?

例如,也许我有一个秒表来告诉您您浏览网页的秒数。有一个“暂停”和“恢复”按钮。 clearInterval() 在这里不起作用的原因是,如果用户在第 40 秒和第 800 毫秒点击“暂停”按钮,当他点击“恢复”按钮时,经过的秒数必须在 200 毫秒后增加 1。如果我在计时器变量上使用 clearInterval() 函数(单击暂停按钮时),然后再次在计时器变量上使用 setInterval() 函数(单击恢复按钮时),经过的秒数将增加1 仅在 1000 毫秒之后,这会破坏秒表的准确性。

那我该怎么做呢?

【问题讨论】:

标签: javascript setinterval


【解决方案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">&nbsp;</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://stackoverflow.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的修改简化版,功能更多。

          完整的库和文档在NPMGITHUB 中提供

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2012-01-22
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多