nothing9 nothing9 - 7 months ago 35
Javascript Question

Is clearTimeout necessary after setTimeout with 0ms?

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?

Answer

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