【问题标题】:Pause and resume setInterval暂停和恢复 setInterval
【发布时间】:2014-07-13 16:32:15
【问题描述】:
window.setInterval(function(){
  //do stuff
}, milisec);

有没有办法随意停止这个间隔,并从它持续的地方恢复它?比如说,代码每 5 秒运行一次。我在第 2 秒中间停止它,当恢复时,我希望它运行剩余的 3 秒,然后每 5 秒继续运行。再次。

【问题讨论】:

  • javascript 是单线程的,setInterval 语句只是指示事件循环在以毫秒(第二个参数)为单位的固定时间后执行一个函数(第一个参数)并立即返回。
  • 我在考虑 clearInterval(),但不确定如何获得剩余时间,从那里恢复并让它继续。
  • 把调用setInterval时的当前时间保存下来,和你“停止”整个过程发生时的当前时间比较。
  • 但是如何恢复间隔运行剩余秒数,然后恢复间隔运行初始计时器?
  • 看看javascript: pause setTimeout(); ...应该很容易适应setInterval

标签: javascript


【解决方案1】:

试试这个:

1- 当您想要pause 计时器时,计算剩余的毫秒数并将其存储在某处,然后调用clearInterval

2- 当您想要resume 计时器时,只需调用setTimeout 将上一步中存储的剩余时间作为参数传递。

3- 在setTimeout 的回调中,您应该再次调用setInterval

更新:这就是你想要的,javascript: pause setTimeout(); 的更改版本,感谢@Felix Kling

    function IntervalTimer(callback, interval) {
        var timerId, startTime, remaining = 0;
        var state = 0; //  0 = idle, 1 = running, 2 = paused, 3= resumed

        this.pause = function () {
            if (state != 1) return;

            remaining = interval - (new Date() - startTime);
            window.clearInterval(timerId);
            state = 2;
        };

        this.resume = function () {
            if (state != 2) return;

            state = 3;
            window.setTimeout(this.timeoutCallback, remaining);
        };

        this.timeoutCallback = function () {
            if (state != 3) return;

            callback();

            startTime = new Date();
            timerId = window.setInterval(callback, interval);
            state = 1;
        };

        startTime = new Date();
        timerId = window.setInterval(callback, interval);
        state = 1;
    }

用法:

    var timer = new IntervalTimer(function () {
        alert("Done!");
    }, 5000);

    window.setTimeout(function () {
        timer.pause();
        window.setTimeout(function () {
            timer.resume();
        }, 5000);
    }, 2000);

【讨论】:

    【解决方案2】:

    为了借鉴 Alireza 的回答,这里有一个 ES6 类,它具有更多功能,但不会立即启动。您可以设置计时器在自动停止之前触发的最大次数,并在下次设置为触发之前暂停和恢复任意次数。

    export default class IntervalTimer{
      constructor(name, callback, interval, maxFires = null){
        this.remaining = 0;
        this.state = 0; //  0 = idle, 1 = running, 2 = paused, 3= resumed
    
        this.name = name;
        this.interval = interval; //in ms
        this.callback = callback;
        this.maxFires = maxFires;
        this.pausedTime = 0; //how long we've been paused for
    
        this.fires = 0;
      }
    
      proxyCallback(){
        if(this.maxFires != null && this.fires >= this.maxFires){
          this.stop();
          return;
        }
        this.lastTimeFired = new Date();
        this.fires++;
        this.callback();
      }
    
      start(){
        this.log.info('Starting Timer ' + this.name);
        this.timerId = setInterval(() => this.proxyCallback(), this.interval);
        this.lastTimeFired = new Date();
        this.state = 1;
        this.fires = 0;
      }
    
      pause(){
        if (this.state != 1 && this.state != 3) return;
    
        this.log.info('Pausing Timer ' + this.name);
    
        this.remaining = this.interval - (new Date() - this.lastTimeFired) + this.pausedTime;
        this.lastPauseTime = new Date();
        clearInterval(this.timerId);
        clearTimeout(this.resumeId);
        this.state = 2;
      }
    
      resume(){
        if (this.state != 2) return;
    
        this.pausedTime += new Date() - this.lastPauseTime;
        this.log.info(`Resuming Timer ${this.name} with ${this.remaining} remaining`);
        this.state = 3;
        this.resumeId = setTimeout(() => this.timeoutCallback(), this.remaining);
      }
    
      timeoutCallback(){
        if (this.state != 3) return;
    
        this.pausedTime = 0;
        this.proxyCallback();
        this.start();
      }
    
      stop(){
        if(this.state === 0) return;
    
        this.log.info('Stopping Timer %s. Fired %s/%s times', this.name, this.fires, this.maxFires);
        clearInterval(this.timerId);
        clearTimeout(this.resumeId);
        this.state = 0;
      }
    
      //set a new interval to use on the next interval loop
      setInterval(newInterval){
        this.log.info('Changing interval from %s to %s for %s', this.interval, newInterval, this.name);
    
        //if we're running do a little switch-er-oo
        if(this.state == 1){
          this.pause();
          this.interval = newInterval;
          this.resume();
        }
        //if we're already stopped, idle, or paused just switch it
        else{
          this.interval = newInterval;
        }
      }
    
      setMaxFires(newMax){
        if(newMax != null && this.fires >= newMax){
          this.stop();
        }
        this.maxFires = newMax;
      }
    }
    

    【讨论】:

    • 刚才看到你的回答了。那是很棒的家伙。谢谢:)
    • 这很棒,但resume() 重置this.fires,这会破坏最大火灾功能。
    • this.log.info 在浏览器上不起作用。我想这是一个服务器端功能?
    • @viv 是的,它是一个自定义记录器。替换为 console.log 或您在前端选择的日志记录。
    【解决方案3】:

    你应该只需要 setTimeout 和 go and stop - http://jsfiddle.net/devitate/QjdUR/1/

    var cnt = 0;
    var fivecnt = 0;
    var go = false;
    
    function timer() {
        if(!go)
            return;
        cnt++;
        if(cnt >= 5){
            cnt=0;
            everyFive();
        }
        jQuery("#counter").text(cnt);
        setTimeout(timer, 1000);
    }
    
    function everyFive(){
        fivecnt++;
        jQuery("#fiver").text(fivecnt);
    }
    
    function stopTimer(){
        go = false;  
    } 
    function startTimer(){
        go = true;
        timer();
    }    
    

    【讨论】:

    • 我们不想要只链接到 jsFiddle 的答案是有原因的。在帖子中包含和解释代码真的有那么难吗?
    • 谢谢,你拯救了我的一天
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-07-19
    • 2015-07-28
    • 2014-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多