14

As i have already learned (here: https://www.youtube.com/watch?v=8aGhZQkoFbQ) it can be useful in some cases to call a setTimeout with 0ms delay (because of the event loop).

Now ususally whenever I use setTimeout I also take care to call clearTimeout at the appropriate spot to make sure nothing remains somewhere and gets executed at a point where I do not want it to be executed.

So my question is: Is it necessary (does it make sense) to call clearTimeout after a setTimeout with 0ms? The passed function is immediately appended to the callback queue so I would assume clearTimeout does not (and cannot) do anything. Or can clearTimeout remove the passed function even from the callback queue (so after the timeout expired but before the function has been executed)?

Second question: Even if it does not do anything, is it anyway 'best practice' to call clearTimeout always in those cases?

user2864740
  • 60,010
  • 15
  • 145
  • 220
nothing9
  • 1,114
  • 1
  • 11
  • 21

5 Answers5

26

Is it necessary (does it make sense) to call clearTimeout after a setTimeout with 0ms?

It is necessary if the goal is to prevent the asynchronous timer callback from running. The execution ordering can be discussed entirely in terms of when the callback is invoked.

First off, the value of 0 milliseconds as a delay means 'run the callback as soon as possible' (from an future asynchronous context), but:

  1. it does not change how setTimeout works; and

  2. 0 is not the actual value used anyway.

The passed function is immediately appended to the callback queue so I would assume clearTimeout does not (and cannot) do anything.

This is incorrect. The passed function is not "immediately appended to the callback queue". Rather, when the timeout expires and the timer is still active, the callback function will be invoked. There may be other asynchronous callbacks - from timers or otherwise - that could be run prior.

Also, all remaining synchronous code is guaranteed to run before the timer callback occurs: clearing the timeout in this context prevents the timer callback from ever being called, irrespective of the time taken in the synchronous code.

Even if it does not do anything, is it anyway 'best practice' to call clearTimeout always in those cases?

Calling clearTimeout will either

  1. prevent the callback from executing, if cleared prior to the callback (as it removes the timer), or;

  2. do nothing if the callback has already occurred (as the timer is no longer active)

Thus clearing the timer is required for correct functioning of the code/algorithm; or it is a useless operation. Creating a timer only to immediately cancel it may be pointless, but that's a digression about code structure..

I also take care to call clearTimeout at the appropriate spot to make sure nothing remains somewhere and gets executed at a point where I do not want it to be executed.

As per above, there is no need to manually clear a timer that is no longer active; and cancelling a timer prior to the invocation of the timer callback will remove the timer and thus prevent the timer callback from executing.

When/where/if a timer should be cancelled depends on the design as a whole.


Cancelling the timeout in the executing code prevents the callback from running: neither the "A" or "B" callback will run.

a = setTimeout(function () { console.log("A"); }, 0);
clearTimeout(a);

b = setTimeout(function () { console.log("B"); }, 0);
s = Date.now()
while (Date.now() - s < 100) { /* waste 100ms of CPU */ }
clearTimeout(b);

Cancelling the timeout in an asynchronous event that runs first prevents the callback from running: the "B" callback will never run:.

a = setTimeout(function () { console.log("A"); clearTimeout(b); }, 0);
b = setTimeout(function () { console.log("B"); }, 0);

While a secondary timer is used (as the ordering is well guaranteed), there is a chance that other asynchronous events (button clicks, web workers, AJAX, etc.) could also occur before a "0ms" timeout.

A clearTimeout invoked after the callback (from any context) is useless:

a = setTimeout(function () { console.log("A"); clearTimeout(a); }, 0);

a = setTimeout(function () { console.log("A"); }, 0);
b = setTimeout(function () { console.log("B"); clearTimeout(a); }, 0);
Community
  • 1
  • 1
user2864740
  • 60,010
  • 15
  • 145
  • 220
  • 3
    Perfect! That was the answer I was looking for. Thanks for all the effort describing all the different cases. Very much appreciated! – nothing9 Sep 16 '15 at 11:42
5

clearTimeout will remove the function. Try this in your Chrome console:

var timer = setTimeout(function() { console.log('hello'); }, 0);
clearTimeout(timer);
Lee
  • 2,993
  • 3
  • 21
  • 27
  • 1
    This is a correct answer. `clearTimeout` will indeed *prevent* the callback from running. even if 'queued' (it is actually queued as soon as the setTimeout call is made), as long as the clear occurs before the callback is actually run by the JavaScript. Also, a setTimeout of 0 has a minimum non-zero value applied.. – user2864740 Sep 15 '15 at 16:54
  • It will display undefined, not hello. – simhumileco Jun 29 '17 at 06:23
  • @simhumileco undefined is the return value from clearTimeout, which illustrates my point that the callback will be prevented from running, as there is nothing logged to the console. – Lee Jun 29 '17 at 07:58
  • OK @Lee it's fine. – simhumileco Jun 29 '17 at 13:59
3

Is it necessary (does it make sense) to call clearTimeout after a setTimeout with 0ms?

No. It is pointless.

The passed function is immediately appended to the callback queue so I would assume clearTimeout does not (and cannot) do anything.

It could do something if you called it in the same function as you set the timeout in the first place, but that would be stupid and you should simply not call setTimeout in the first place if you were doing to do that.

Even if it does not do anything, is it anyway 'best practice' to call clearTimeout always in those cases?

No. It is pointless.

I also take care to call clearTimeout at the appropriate spot to make sure nothing remains somewhere and gets executed at a point where I do not want it to be executed.

The key phrase here is "the appropriate spot". There is no generically appropriate spot for calling clearTimeout.

Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335
  • You can also add this: `Even if it does not do anything, is it anyway 'best practice' to call clearTimeout always in those cases?` -- If you know when to clear the timeout, you wouldn't use the `setTimeout`s. You'll simply call your function from there. – Tushar Sep 15 '15 at 08:24
  • Hi and thanks for your answer! Your second comment makes a lot of sense (and btw. I do not call `setTimeout` and `clearTimeout` inside the same function). But is this really limited to function scope? What if the call stack does not get empty for a while for some other reason (other async calls)? – nothing9 Sep 15 '15 at 08:54
  • Do you have code that is experiencing a specific race condition? Or that you think might suffer from one? Trying to deal with vague generalities just complicates things. – Quentin Sep 15 '15 at 09:02
  • I asked because I am working on a single page application using a modular approach and require.js and I am only delevoping single modules. So there are a lot of things I do not have any influence on... so theoretically it could happen that the call stack gets filled up somewhere else. But I agree that in this case there would be a different (and way more serious problem). Anyway, Thanks. – nothing9 Sep 15 '15 at 09:35
1

No, you don't need to use clearTimeout if you intend for the timeout to be executed.

The clearTimeout method is only used to stop timeouts from being executed. You can stop a timeout even if it has a time of 0 ms, because it won't be executed until you exit your function and return the control to the browser.

Guffa
  • 687,336
  • 108
  • 737
  • 1,005
0

const timeout = setTimeout(() => {
    console.log('sdfsdf');
    clearTimeout(timeout);
}, 0);
  • Firstly, don't put JS code in html section. Then, can you explain why this code will fix the issue ? It seems to be a duplicate of [this answer](https://stackoverflow.com/a/32581063/10952503) with just less explainations – Elikill58 Jan 18 '22 at 09:26