138

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?

Soviut
  • 88,194
  • 49
  • 192
  • 260
Hailwood
  • 89,623
  • 107
  • 270
  • 423
  • 1
    For those that are wondering, The pausing is for eg: a div is set to disappear in 5 seconds, at 3 seconds (so 2 seconds left) the user mouses over the div, you pause the timeout, once the user mouses off the div you resume it, 2 seconds later it disappears. – Hailwood Oct 19 '10 at 15:39

20 Answers20

305

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

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();
Tim Down
  • 318,141
  • 75
  • 454
  • 536
  • 3
    @yckart: Rolled back, sorry. It's a good addition except that adding additional parameters to `setTimeout()` doesn't work in Internet Explorer <= 9. – Tim Down Mar 31 '13 at 12:51
  • @TimDown Mhh, ok. But, who cares *oldIE*?! :P And BTW, `setTimeout` works even with `arguments` in `IE lt 9`: http://fiddle.jshell.net/kx9VU/ – yckart Mar 31 '13 at 12:56
  • @yckart: IE 8 is still very much around, unfortunately. Your jsFiddle doesn't prove anything; did you mean something like this? http://jsfiddle.net/adwKX/ – Tim Down Mar 31 '13 at 13:48
  • Nope, as I said: http://fiddle.jshell.net/kx9VU/1/ works well for me, even in IE8. Look at my approach: http://stackoverflow.com/a/15729885/1250044 – yckart Mar 31 '13 at 13:53
  • @yckart: That still doesn't prove anything: you're only passing two parameters to `setTimeout()` in your jsFiddle. – Tim Down Mar 31 '13 at 14:07
  • @yckart: Ah, I see: you're not using the third parameter of `setTimeout()` any more. Yes, that makes more sense now. – Tim Down Mar 31 '13 at 14:09
  • Yeah, I use the invocation braces to pass extra arguments, a simple but functional way to solve this problem ;) – yckart Mar 31 '13 at 14:10
  • There is a problem when resuming your Timer twice. the timeout ID has to be cleared first (just in case). – kernel Aug 14 '13 at 13:11
  • @kernel: Could you elaborate? It looks fine to me. – Tim Down Aug 14 '13 at 13:36
  • 6
    If you do `timer.resume(); timer.resume();` you'll end up having two timeouts in parallel. That's why you would want to either `clearTimeout(timerId)` first or do a `if (timerId) return;` short circuit at the very beginning of resume. – kernel Aug 14 '13 at 15:06
  • @kernel: True. I hadn't really considered that case for this minimal example. – Tim Down Aug 14 '13 at 17:05
  • +1 for simplicity. but prototype must be used for method definitions. Your code creates pause and resume functions for each instance. – Umur Gedik Feb 20 '14 at 16:46
  • @UmurGedik: Whether that approach is preferable depends on the context, I'd say. For this code, for example, defining methods on `Timer`'s prototype would have to come at the cost of exposing internals such as the start time and callback as properties. In general, I'm a big fan of defining methods on prototypes. – Tim Down Feb 21 '14 at 01:22
  • 2
    Hey, I liked this answer, but I had to pull: `var timerId, start, remaining;` outside the Class scope and add `remaining = delay;` back inside to catch the parameter. Works like a charm tho! – phillihp Nov 11 '14 at 03:46
  • @phillihp: Why did you need to move the `var` statement? – Tim Down Nov 11 '14 at 10:22
  • As Philliph also mentioned, I had to pull the variables outside the class to get it to work. Not really sure why this makes it work, but it was the only way I got it to behave as intended... Thanks Tim Down for the answer and @phillihp for the modification. – Josh979 Feb 17 '16 at 23:44
  • 1
    @Josh979: You really don't need to do that and it's a bad idea to do it because it exposes variables that should be internal. Maybe you've pasted the code inside a block (e.g. inside an `if (blah) { ... }`) or something? – Tim Down Feb 18 '16 at 12:19
  • @josh979 and TimDown - Using "var" will limit the scope to the context, where not using "var" will allow immediate global. You could still use the "var" declaration outside of the function to have larger scope access. Also depends on how the browsers ECMA JavaScript is interpreted. I would always say limit scope and not use global variables when possible. – phillihp Feb 19 '16 at 04:10
  • 2
    For some reason this only runs once for me after being initialised. – peterxz Jul 29 '18 at 13:38
  • I had to make some changes to this for it to work with multiple timers. I created the internal function variables such as "this.timerId=delay..." In addition, I added a this.finished variable and ran the resume block iff this.finished is false. this.finished becomes true in the pause block if remaining < 0 – joe muoio Nov 08 '19 at 17:27
  • @joemuoio You shouldn't need to do that. Have you included the function inside a block (e.g. inside an `if (blah) { ... }`)? – Tim Down Nov 11 '19 at 10:34
  • 1
    @joemuoio I have now changed it from a function declaration to a function expression, which means the code will work as intended inside a block. – Tim Down Nov 11 '19 at 10:37
  • thanks for the update on this! Definitely is more elegant than my modifications. – joe muoio Nov 18 '19 at 19:26
  • @peterxz Same for me (runs only once)! – John Dec 30 '19 at 19:46
  • @John: What do you mean? The timer is designed to run to completion once and not be reused. It's essentially a pausable version of `setTimeout`. – Tim Down Jan 03 '20 at 15:39
  • If I could upvote more than once I would, Thank you. – Andres Felipe May 13 '20 at 23:14
  • 1
    @kernel `clearTimeout(timerId)` in `resume` is not correct because if suppose `resume` is called once and then after some time (_w/o calling `pause` in b/w_) its called again, then the current timer is cleared (_which solves the issue of multiple timers_) but the new timer that is created loses all the progress made and sets the timer with the old `remaining` value. So, its better to early return and I've updated the answer with the same. – Som Shekhar Mukherjee Dec 18 '21 at 13:32
20

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 };
}
yckart
  • 32,460
  • 9
  • 122
  • 129
Sean Vieira
  • 155,703
  • 32
  • 311
  • 293
10

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;
Community
  • 1
  • 1
yckart
  • 32,460
  • 9
  • 122
  • 129
9

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

RoToRa
  • 37,635
  • 12
  • 69
  • 105
7

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.

TheCrzyMan
  • 1,010
  • 1
  • 11
  • 25
  • Can you explain your thoughts, why we need a complex *Class* to pause a `setInterval`? I think a simple `if(!true) return;` will do the trick, or am I wrong? – yckart Mar 31 '13 at 15:32
  • 2
    I made it so that you can literally pause the interval, instead of just skipping a call when it triggers. If, in a game, a power-up is released every 60 seconds, and I pause the game just before it is about to trigger, using your method, I will have to wait for another minute for another power-up. That isn't truly pausing, that is just ignoring a call. Instead, My method is actually pausing, and therefore, the power-up is released 'on-time' in respect to game-play. – TheCrzyMan Aug 07 '13 at 14:32
7

"Pause" and "resume" don't really make much sense in the context of setTimeout, which is a one-off thing. You might want to pause a chained series of setTimeout calls, in which case just don't schedule the next one (perhaps cancel the one that's outstanding via clearTimeout, as below). But setTimeout itself doesn't loop, there's nothing to pause and resume.

If you mean setInterval then 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);
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
4

/revive

ES6 Version using Class-y syntactic sugar

(slightly-modified: added 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>
jeremiah.trein
  • 776
  • 6
  • 6
3

Typescript implementation based on top rated answer

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

}
cuddlemeister
  • 1,586
  • 12
  • 15
2

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();
    });
Ashraf Fayad
  • 1,433
  • 4
  • 17
  • 31
2
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();

The programme is rather simple. We will create a class containing two functions, the pause function and the unpause function.

The pause function will clear the setTimeout and store the time that has elapsed between the start and now in the time_left variable. The unpause function will recreate a setTimeout by putting the time_left time as an argument.

1

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.

B Robster
  • 40,605
  • 21
  • 89
  • 122
  • I was hoping doTimeout would have pause/resume, but I'm not seeing it when looking at the full documentation, looping examples, and even source. The closest to pause I could see is cancel, but then I'd have to recreate the timer with function again. Did I miss something? – ericslaw Aug 26 '12 at 08:56
  • Sorry to lead you down the wrong path. I've removed that inaccuracy from my answer. – B Robster Aug 28 '12 at 14:33
1

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.

Fabien Snauwaert
  • 4,995
  • 5
  • 52
  • 70
1

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>
John Hartsock
  • 85,422
  • 23
  • 131
  • 146
1

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

};
meleyal
  • 32,252
  • 24
  • 73
  • 79
1
function delay (ms)   {  return new Promise(resolve => setTimeout(resolve, s));  }

"async" working demo at: site zarsoft.info

Joe Oliver
  • 36
  • 3
1

You can do like below to make setTimeout pausable on server side (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();
};

and you can check it as below

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)
Manoj Rana
  • 3,068
  • 1
  • 24
  • 34
0

If anyone wants the TypeScript version shared by the Honorable @SeanVieira here, you can use this:

    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;
    }
1antares1
  • 1,146
  • 2
  • 11
  • 19
0

I created this code in TypeScript for slider feature:

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;
    }
  }
}
Damian
  • 602
  • 7
  • 18
-2

I don't think you'll find anything better than clearTimeout. Anyway, you can always schedule another timeout later, instead 'resuming' it.

Nikita Rybak
  • 67,365
  • 22
  • 157
  • 181
-2

If you have several divs to hide, you could use an setInterval and a number of cycles to do like in:

<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>
Mic
  • 24,812
  • 9
  • 57
  • 70