143

Is there a way to clear all time outs from a given window? I suppose the timeouts are stored somewhere in the window object but couldn't confirm that.

Any cross browser solution is welcome.

vsync
  • 118,978
  • 58
  • 307
  • 400
marcio
  • 10,002
  • 11
  • 54
  • 83
  • 1
    possible duplicate of [How to stop all timeouts and intervals using javascript?](http://stackoverflow.com/questions/3141064/how-to-stop-all-timeouts-and-intervals-using-javascript) – Bernhard Hofmann Feb 27 '15 at 07:37
  • I searched for this and found both. Yours was older so I thought it'd be good housekeeping to reduce the two questions to one. Mine is just one vote; quite a few more people will need to vote to close, and the duplicate link might help others. – Bernhard Hofmann Feb 27 '15 at 10:43
  • 2
    @Bernard Hofmann I'm not sure if it counts as a duplicate question if this one got a better answer. The accepted answer on that one from 2010 was "No". – RoboticRenaissance Jan 21 '17 at 15:06
  • It would be a bad idea to do this in a system of any reasonable size. You could kill tasks in totally unrelated parts of the system that you don't know about. – James Sep 10 '21 at 13:31

13 Answers13

196

They are not in the window object, but they have ids, which (afaik) are consecutive integers.

So you may clear all timeouts like so:

var id = window.setTimeout(function() {}, 0);

while (id--) {
    window.clearTimeout(id); // will do nothing if no timeout with id is present
}
user123444555621
  • 148,182
  • 27
  • 114
  • 126
  • 3
    Is this part of the specification, or could it be implementation-dependent? – Tikhon Jelvis Jan 14 '12 at 04:47
  • It's definitely implementation-dependent. But all browsers that I know of use some incrementation mechanism. May not start at 1 though. – user123444555621 Jan 14 '12 at 04:56
  • 8
    It need not start at 1. The [HTML5](http://www.w3.org/TR/html5/timers.html#timers) spec says "Let handle be a user-agent-defined integer that is greater than zero that will identify the timeout to be set by this call." which leaves room for the handle to be any positive integer including non-consecutive and non-small integers. – Mike Samuel Jan 14 '12 at 04:59
  • 1
    Keep in mind that older browsers may (again, afaik they don't) use arbitrary ids according to the [previous specs](http://www.w3.org/TR/Window/#window-timers) – user123444555621 Jan 14 '12 at 05:04
  • 4
    That's very clever, but the OP should probably ask if there is a better solution that keeps track of active timers. – Jason Harwig Jan 14 '12 at 05:05
  • 3
    Isn't this an infinite loop? Not all browsers render if(0) as false. – Travis J Jan 14 '12 at 05:10
  • 3
    Since I asked how to clear ALL timeouts, I'm accepting this answer. Very clever solution. – marcio Jan 14 '12 at 05:56
  • I'm not sure about this method for solving the problem. Consider there are 10 timeout instances and I doubt all the instances can have the same id. So the while method fails here. – Rupam Datta Mar 10 '14 at 05:03
  • 1
    @Rupam What are you talking about? This solution just loops down from a freshly assigned handle all the way to 0. Of course, it assumes that handles are assigned in increasing order. – Schism Jun 20 '14 at 17:16
  • The answer below by "Michael Berkowski" is better. – Danish Adeel Sep 01 '15 at 07:40
  • @AndreFigueiredo - Probably some old browser that no one uses anymore, I cannot remember since it was 6 years ago. Maybe IE6? An early version of Safari Mobile? Who knows, this should be relatively safe to use today, although I would recommend going from 0 to id in a for loop instead of this while loop shown to be safe. – Travis J Feb 21 '18 at 23:17
  • This answer is not correct because it returns infinite loop! I aggree with @TravisJ – saulyasar Mar 09 '18 at 11:36
  • This doesn't work. how are you going to force window.setTimeout to throw that ID? what if there are multiple? – guest Apr 23 '19 at 04:42
  • Note that in nodeJS, setTimeout returns a Timeout object and not a number (same for setInterval). This only work in browser. – Lionep Nov 21 '19 at 13:29
  • you are the boss dude – Bri4n Apr 24 '20 at 04:07
  • 1
    @TravisJ @saulyasar This is simply not true, `while(i--)` is a very common pattern and will always terminate when it reaches `0`. `0` has always been falsey in JS (and many other languages for that matter): https://tc39.es/ecma262/#sec-toboolean – kueblc Dec 30 '20 at 19:12
  • 1
    @kueblc - That assumes the browser is actually calling toboolean before evaluating. Obviously modern browsers do this, but at the time I wrote that comment, browsers from 2004 and such were still being used to a small degree, and there were some really funky ones out there, such as Nokia's mobile browser. Needless to say, you are probably safe in 2020 to use while(0) and not end up with an infinite loop. – Travis J Dec 30 '20 at 19:34
  • I had a similar problem, and tried this solution. I agree that it's smart, but it didn't work for me. The assigned variable increased by two every time a new timeout was created (why by 2 is another problem) The answer below from Michael Berkowski solved my issue. So for me it seems, that though all timeout-instances are handled because the loop is working from the highest given integer downwards, not every timeout is cleared, why ever. – wolf Jul 28 '21 at 12:15
109

I think the easiest way to accomplish this would be to store all the setTimeout identifiers in one array, where you can easily iterate to clearTimeout() on all of them.

var timeouts = [];
timeouts.push(setTimeout(function(){alert(1);}, 200));
timeouts.push(setTimeout(function(){alert(2);}, 300));
timeouts.push(setTimeout(function(){alert(3);}, 400));

for (var i=0; i<timeouts.length; i++) {
  clearTimeout(timeouts[i]);
}
Michael Berkowski
  • 267,341
  • 46
  • 444
  • 390
  • 21
    This has the bonus (or potential drawback, I guess) of only clearing the ones *you* set, so you won't inadvertently break outside code. – Tikhon Jelvis Jan 14 '12 at 04:48
  • 2
    +1, will not clear all timeouts but is a good solution to clear a specific group of timeouts at once – marcio Jan 14 '12 at 05:58
  • 1
    This is the best solution because it won't break external code, but since I asked how to clear all timeouts I ended up accepting @Pumbaa80 answer :) – marcio Jun 10 '14 at 23:20
  • 2
    @marcioAlmada Strange, but cool to see you return to comment on this again 2.5yrs later :) – Michael Berkowski Jun 11 '14 at 00:58
  • Lets be real here, a push down stack is clearly the proper answer both in terms of spec compliance and code stability... – lol Jul 11 '14 at 04:44
  • This is the best solution, IMO, for almost all cases. Timeout objects require, in modern computing, like zero resources. If you need hyper-uber-omg-efficiency the answer @TravisJ suggested might be better but in 99.99% of cases you'll notice absolutely no difference. – dudewad Sep 30 '14 at 18:55
  • 1
    This is the worst solution, IMO, for almost all cases. This answer does not address the question. I absolutely need to clear a timeout that I didn't set. We have some embedded code that sets timeouts, which we need to clear until the embedded code can be fixed, and flashed. – Dustin Graham Sep 13 '15 at 21:39
  • 7
    Perhaps I'd like to clear all timeouts and intervals when I enter a webpage I do not control, because they have an annoying pop up ad that doesn't start until after my adblocker runs. – RoboticRenaissance Jan 21 '17 at 14:57
  • 9 years later I found this is my perfect solution. Thank you @MichaelBerkowski – wolf Jul 28 '21 at 12:24
19

This is very late... but:

Basically, setTimeout/setInterval ID's go in consecutive order, so just create a dummy timeout function to get the highest ID, then clear interval on all the IDs lower than that.

const highestId = window.setTimeout(() => {
  for (let i = highestId; i >= 0; i--) {
    window.clearInterval(i);
  }
}, 0);
charri
  • 984
  • 7
  • 9
13

I have an addition to Pumbaa80's answer that might be useful for someone developing for old IEs.

Yes, all major browsers implement timeout ids as consecutive integers (which is not required by specification). Althrough the starting number differs form browser to browser. It seems that Opera, Safari, Chrome and IE > 8 starts timeout ids from 1, Gecko-based browsers from 2 and IE <= 8 from some random number that is magically saved across tab refresh. You can discover it yourself.

All that meens that in IE <=8 the while (lastTimeoutId--) cycle may run over 8digits times and show the "A script on this page is causing Internet Explorer to run slowly" message. So if you can not save all you timeout id's or don't want to override window.setTimeout you may consider saving the first timeout id on a page and clearing timeouts until it.

Execute the code on early page load:

var clearAllTimeouts = (function () {
    var noop = function () {},
        firstId = window.setTimeout(noop, 0);
    return function () {
        var lastId = window.setTimeout(noop, 0);
        console.log('Removing', lastId - firstId, 'timeout handlers');
        while (firstId != lastId)
            window.clearTimeout(++firstId);
    };
});

And then clear all pending timeouts that probably was set by foreign code so many times you want

Community
  • 1
  • 1
Kolya Ay
  • 353
  • 3
  • 8
10

How about store the timeout ids in a global array, and define a method to delegate the function call to the window's.

GLOBAL={
    timeouts : [],//global timeout id arrays
    setTimeout : function(code,number){
        this.timeouts.push(setTimeout(code,number));
    },
    clearAllTimeout :function(){
        for (var i=0; i<this.timeouts.length; i++) {
            window.clearTimeout(this.timeouts[i]); // clear all the timeouts
        }
        this.timeouts= [];//empty the id array
    }
};
JaskeyLam
  • 15,405
  • 21
  • 114
  • 149
8

You have to rewrite the window.setTimeout method and save its timeout ID.

const timeouts = [];
const originalTimeoutFn = window.setTimeout;

window.setTimeout = function(fun, delay) { //this is over-writing the original method
  const t = originalTimeoutFn(fn, delay);
  timeouts.push(t);
}

function clearTimeouts(){
  while(timeouts.length){
    clearTimeout(timeouts.pop());
  }
}
guest
  • 2,185
  • 3
  • 23
  • 46
7

To clear all timeouts they must be "captured" first:

Place the below code before any other script and it will create a wrapper function for the original setTimeout & clearTimeout.

New clearTimeouts methods will be added to the window Object, which will allow clearing all (pending) timeouts (Gist link).

Other answers lack complete support for possible arguments setTimeout might receive.

// isolated layer wrapper (for the local variables)
(function(_W){

  var cache = [],                // will store all timeouts IDs
      _set = _W.setTimeout,      // save original reference
      _clear = _W.clearTimeout  // save original reference
  
  // Wrap original setTimeout with a function 
  _W.setTimeout = function( CB, duration, arg ){
      // also, wrap the callback, so the cache reference will be removed 
      // when the timeout has reached (fired the callback)
      var id = _set(function(){
          removeCacheItem(id)
          CB.apply(null, arguments)
      }, duration || 0, arg)
  
      cache.push(id) // store reference in the cache array
  
      // id reference must be returned to be able to clear it 
      return id
  }
  
  // Wrap original clearTimeout with a function 
  _W.clearTimeout = function( id ){
      _clear(id)
      removeCacheItem(id)
  }
  
  // Add a custom function named "clearTimeouts" to the "window" object
  _W.clearTimeouts = function(){
      console.log("Clearing " + cache.length + " timeouts")
      cache.forEach(n => _clear(n))
      cache.length = []
  }
  
  // removes a specific id from the cache array 
  function removeCacheItem( id ){
      var idx = cache.indexOf(id)
      
      if( idx > -1 )
          cache = cache.filter(n => n != id )
  }
  
})(window);

// lets define some timeouts

setTimeout(()=> console.log('1s passed'), 1000); // should run
setTimeout(()=> console.log('2s passed'), 2000); // should be cleared
setTimeout(()=> console.log('3s passed'), 3000); // should be cleared


// lets clear them all after 1 and a half second:
setTimeout(()=> {
  window.clearTimeouts()
}, 1500)

See a nice piece of code which I named "better-timeout" which does a bit more than the above.

vsync
  • 118,978
  • 58
  • 307
  • 400
  • 1
    This is the most correct and complete answer. – Achyut Rastogi Jan 25 '22 at 17:21
  • After you override the window.setTimeout, you put in danger all timeouts from third party scripts and your own env. scripts like reacts / next' / vues etc. nice code but you can add 'context' and 'capture' only timeouts from that 'context' And for extra you get the ability to clear by context or all that have a context Doing that you ensue you remove only timeouts you defined. – Itay Merchav Sep 12 '22 at 05:56
  • @ItayMerchav - true but it is way beyond the scope of this question here. You would have to modify the `setTimeout` syntax and introduce another parameter to let my script above "know" about this special scope. – vsync Sep 12 '22 at 07:59
2

For completeness, I wanted to post a general solution that covers both setTimeout and setInterval.

It seems browsers might use the same pool of IDs for both, but from some of the answers to Are clearTimeout and clearInterval the same?, it's not clear whether it's safe to rely on clearTimeout and clearInterval performing the same function or only working on their respective timer types.

Therefore, when the goal is to kill all timeouts and intervals, here's an implementation that might be slightly more defensive across implementations when unable to test all of them:

function clearAll(windowObject) {
  var id = Math.max(
    windowObject.setInterval(noop, 1000),
    windowObject.setTimeout(noop, 1000)
  );

  while (id--) {
    windowObject.clearTimeout(id);
    windowObject.clearInterval(id);
  }

  function noop(){}
}

You can use it to clear all timers in the current window:

clearAll(window);

Or you can use it to clear all timers in an iframe:

clearAll(document.querySelector("iframe").contentWindow);
Chris Calo
  • 7,518
  • 7
  • 48
  • 64
  • keep in mind that this approach destroys vue-cli-service watcher so you cannot do hot-reload when cleaning all timeouts and timeintervals. I assume it is the same for other hot reload watchers for frameworks such as (angular, react, ember etc) – Michail Michailidis Feb 17 '20 at 11:48
1

I use Vue with Typescript.

    private setTimeoutN;
    private setTimeoutS = [];

    public myTimeoutStart() {

        this.myTimeoutStop();//stop All my timeouts

        this.setTimeoutN = window.setTimeout( () => {
            console.log('setTimeout');
        }, 2000);

        this.setTimeoutS.push(this.setTimeoutN)//add THIS timeout ID in array

    }

    public myTimeoutStop() {

        if( this.setTimeoutS.length > 0 ) {
            for (let id in this.setTimeoutS) {
                console.log(this.setTimeoutS[id]);
                clearTimeout(this.setTimeoutS[id]);
            }
            this.setTimeoutS = [];//clear IDs array
        }
    }
LuanLuanLuan
  • 523
  • 5
  • 10
0

We've just published a package solving this exact issue.

npm install time-events-manager

With that, you can view all timeouts and intervals via timeoutCollection & intervalCollection objects. There's also a removeAll function which clears all timeouts/intervals both from the collection and the browser.

Bar Goldinfeld
  • 183
  • 1
  • 2
  • 12
0

Inside the setTimeout after the definition of function f(...){} and timeout can be additional parameters.

For example: $setTimeout( function(a, b){ run(a); run(b); }, 100, a, b); )

...args solves that problem.

    var $timeouts = new Array();

    function $setTimeout(...args) 
    {
      var t = window.setTimeout(...args);
      $timeouts.push(t);
      return t;
    }

    function $clearTimeout(id) 
    {
        if( $timeouts.indexOf(id) > -1 )
            $timeouts = $timeouts.filter(n => n != id )

        window.clearTimeout(id);
    }


    function $clearTimeouts()
    {
        while($timeouts.length)
           window.clearTimeout($timeouts.pop());
    }

For old browsers you need to use another methods to pass parameters and to delete a value from the array $timeouts.

var $timeouts = new Array();

function $setTimeout() 
{
  var t = window.setTimeout.apply( this, arguments );
  $timeouts.push(t);
  return t;
}

function $clearTimeout(id) 
{
    var index = $timeouts.indexOf(id);

    if (index > -1) {
       $timeouts.splice(index, 1);
    }

    window.clearTimeout(id);
}


function $clearTimeouts()
{
    while($timeouts.length)
       window.clearTimeout($timeouts.pop());
}
0

The answers of the other are actually correct. But for me this is how approach this kind of stuff. Check my code bellow.


// example when I person types on search input
function typeOnSearch(time = 3000) {
  // this will clear timeout
  clearTimeout(window.typeOnSearchTimeOut); 

  // we create a timeout variable and added it to window, this way, we can access this in any function in our app.
  window.typeOnSearchTimeOut = setTimeout( () => {
    //enter what you like to do here, like fetch data
  }, time);
}

Jenuel Ganawed
  • 358
  • 6
  • 15
0

Use a global timeout which all of your other functions derive timing from. This will make everything run faster, and be easier to manage, although it will add some abstraction to your code.

Travis J
  • 81,153
  • 41
  • 202
  • 273
  • I don't fully understand you. You mean to extend the behavior of the `set_timeout` global function? Could you give an example code? – marcio Jan 17 '12 at 18:27
  • 1
    I just meant that you have one global timeout running once every 50ms. Every other function which would require a timing element would then grab it from the global timeout. Google switched to this for efficiency, although I can no longer find the article quoting it. – Travis J Jan 17 '12 at 18:44