[removed] pause setTimeout();

2019-01-01 12:37发布

问题:

If I have an active timeout running that was set through var t = setTimeout(\"dosomething()\", 5000),

Is there anyway to pause and resume it?


Is there any way to get the time remaining on the current timeout?
or do I have to in a variable, when the timeout is set, store the current time, then we we pause, get the difference between now and then?

回答1:

You could wrap window.setTimeout like this, which I think is similar to what you were suggesting in the question:

function Timer(callback, delay) {
    var timerId, start, remaining = delay;

    this.pause = function() {
        window.clearTimeout(timerId);
        remaining -= new Date() - start;
    };

    this.resume = function() {
        start = new Date();
        window.clearTimeout(timerId);
        timerId = window.setTimeout(callback, remaining);
    };

    this.resume();
}

var timer = new Timer(function() {
    alert(\"Done!\");
}, 1000);

timer.pause();
// Do some stuff...
timer.resume();


回答2:

Something like this should do the trick.

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:

No. You\'ll need cancel it (clearTimeout), measure the time since you started it and restart it with the new time.



回答4:

A slightly modified version of Tim Downs answer. However, since Tim rolled back my edit, I\'ve to answer this myself. My solution makes it possible to use extra arguments as third (3, 4, 5...) parameter and to clear the timer:

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

As Tim mentioned, extra parameters are not available in IE lt 9, however I worked a bit around so that it will work in oldIE\'s too.

Usage: 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:

\"Pause\" and \"resume\" don\'t really make much sense in the context of setTimeout, which is a one-off thing. Do you mean setInterval? If so, no, you can\'t pause it, you can only cancel it (clearInterval) and then re-schedule it again. Details of all of these in the Timers section of the spec.

// 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:

The Timeout was easy enough to find a solution for, but the Interval was a little bit trickier.

I came up with the following two classes to solve this issues:

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

These can be implemented as such:

var pt_hey = new PauseableTimeout(function(){
    alert(\"hello\");
}, 2000);

window.setTimeout(function(){
    pt_hey.pause();
}, 1000);

window.setTimeout(\"pt_hey.start()\", 2000);

This example will set a pauseable Timeout (pt_hey) which is scheduled to alert, \"hey\" after two seconds. Another Timeout pauses pt_hey after one second. A third Timeout resumes pt_hey after two seconds. pt_hey runs for one second, pauses for one second, then resumes running. pt_hey triggers after three seconds.

Now for the trickier intervals

var pi_hey = new PauseableInterval(function(){
    console.log(\"hello world\");
}, 2000);

window.setTimeout(\"pi_hey.pause()\", 5000);

window.setTimeout(\"pi_hey.resume()\", 6000);

This example sets a pauseable Interval (pi_hey) to write \"hello world\" in the console every two seconds. A timeout pauses pi_hey after five seconds. Another timeout resumes pi_hey after six seconds. So pi_hey will trigger twice, run for one second, pause for one second, run for one second, and then continue triggering every 2 seconds.

OTHER FUNCTIONS

  • clearTimeout() and clearInterval()

    pt_hey.clearTimeout(); and pi_hey.clearInterval(); serve as an easy way to clear the timeouts and intervals.

  • getTimeLeft()

    pt_hey.getTimeLeft(); and pi_hey.getTimeLeft(); will return how many milliseconds till the next trigger is scheduled to occur.



回答7:

I needed to calculate the elapsed and remaining time to show a progress-bar. It was not easy using the accepted answer. \'setInterval\' is better than \'setTimeout\' for this task. So, I created this Timer class that you can use in any project.

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


回答8:

You could look into clearTimeout()

or pause depending on a global variable that is set when a certain condition is hit. Like a button is pressed.

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

  <script>
  var myBool = false;

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


回答9:

You could also implement it with events.

Instead of calculating the time difference, you start and stop listening to a \'tick\' event which keeps running in the background:

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

};


回答10:

If you\'re using jquery anyhow, check out the $.doTimeout plugin. This thing is a huge improvement over setTimeout, including letting you keep track of your time-outs with a single string id that you specify and that doesn\'t change every time you set it, and implement easy canceling, polling loops & debouncing, and more. One of my most-used jquery plugins.

Unfortunately, it doesn\'t support pause/resume out of the box. For this, you would need to wrap or extend $.doTimeout, presumably similarly to the accepted answer.



回答11:

I needed to be able to pause setTimeout() for slideshow-like feature.

Here is my own implementation of a pausable timer. It integrates comments seen on Tim Down\'s answer, such as better pause (kernel\'s comment) and a form of prototyping (Umur Gedik\'s comment.)

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

Example:

var timer = new Timer( function(){ console.log( \'hey! this is a timer!\' ); }, 10000 );
timer.pause();

To test the code out, use timer.resume() and timer.pause() a few times and check how much time is left. (Make sure your console is open.)

Using this object in place of setTimeout() is as easy as replacing timerID = setTimeout( mycallback, 1000) with timer = new Timer( mycallback, 1000 ). Then timer.pause() and timer.resume() are available to you.



回答12:

/revive

ES6 Version using Class-y syntactic sugar