Tuesday, 12 June, 2018 UTC


Summary

In browser environments, as well as with Node.js, we get two methods on the global object that make it easy to schedule tasks to be performed at a later time: setTimeout and setInterval.
  • setTimeout: Calls a function once after a specified delay.
  • setInterval: Calls a function continuously at a specified delay between each call.
setTimeout
The setTimeout method expects 2 arguments: a reference to a callback function and a delay in milliseconds. The following will print a message to the console after 1 second:
setTimeout(() => { console.log('Alligator!!!!'); }, 1000);
The above example uses an inline function expression, but we could just as well reference a function by its name:
function gator() { console.log('Alligator!!!!'); } setTimeout(gator, 1000);
Note that the function shouldn't be invoked, but instead a reference to the function should be used.

Additional arguments

setTimeout can take additional arguments after the delay and the extra values will be passed-in to the callback function:
function animal(animalName, extras = '!!!') { const name = animalName.charAt(0).toUpperCase() + animalName.slice(1); console.log(`${name}${extras}`); } setTimeout(animal, 2500, 'wolf', ' $#@%'); // Wolf $#@% (after 2.5 seconds)

Cancelling a timer

The setTimeout method returns a timer id that can then be passed to the global clearTimeout method to cancel the timeout. Take the following example:
function animal(animalName, extras = '!!!') { const name = animalName.charAt(0).toUpperCase() + animalName.slice(1); console.log(`${name}${extras}`); } const animalTimer = setTimeout(animal, 800, 'wolf', ' $#@%'); setTimeout(() => { clearTimeout(animalTimer); }, 250);
With this, nothing will be printed to the console, because a second timer cancels the first one before the first one has a chance to run.
Execution with 0ms delay
A popular technique to improve the performance of JavaScript code is to set timers with setTimeout calls that have a delay of 0ms. This adds a timer to the message queue for code to run ASAP, as soon as the current synchronous code and previous messages in the queue have finished executing, making the code executed with the setTimeout asynchronous:
setTimeout(() => { console.log('Panda 🐼'); }, 0); console.log('Koala 🐨'); // Koala 🐨 // Panda 🐼
Used this way, setTimeout can become useful to defer tasks that could otherwise be blocking.
Something to keep in mind here is that this also means that the delay specified with setTimeout is not a guarantee, and only specifies the minimum amount of time that has to pass. If the JS engine is busy, the code could be executed much later.
Note also that when using a value of 0 as the delay, the second argument to setTimeout can be omitted and 0 will be implied.
setInterval
The API for setInterval is pretty much the same as with setTimeout. Just like with setTimeout:
  • It expects 2 arguments: a callback function and a delay in milliseconds.
  • It returns a timer ID, and clearInterval can be called with the timer ID to cancel the timer.
  • Additional arguments can be passed-in, and they will then be passed-in as arguments to the callback function.
The difference is that setInterval calls the callback function repeatedly with the specified delay in-between the calls. In the following example, we increment a value and print it to the console every second:
let i = 0; function increment() { i++; console.log(i); } setInterval(increment, 1000); // 1 // 2 // 3 // ...

Recursive setTimeout Calls

Sometimes it can make more sense to use a recursive setTimeout timer instead of a setInterval timer. For one, if the tasks performed as part of the timer take a long time, using setInterval can mean that the tasks end up having no delay in-between because the delay is calculated from the start of the task. If using a recurring setTimeout timer instead, we have full control on when the delay starts. Here’s an example of a recursive setTimeout call:
let i = 0; function increment() { i++; console.log(i); } let timer = setTimeout(function myTimer() { increment(); timer = setTimeout(myTimer, 1000); }, 1000); // let's cancel after 7 seconds setTimeout(() => { console.log('Cancelling'); clearTimeout(timer); }, 7000);
Note how we’re re-assigning the value of timer each time, so that we can still probably cancel the timer when needed.