It’s good to have simple, and straightforward code execution rules. Top to bottom left to right. Everything is simple, everything is clear.

But, if the current means are not enough to complete the task, then new rules need to be introduced.

Delayed execution and setTimeout

Sometimes we want to do something, but not immediately.

For example, we have a taxi call function callTaxi(destination). It takes one parameter - the destination and instantly gives a taxi for a trip to the specified address. We need to go to the airport. Not right now, but in 5 hours.

To delay the call of a function, there is a setTimeout(callback, delay) function in JS. It takes two parameters:

  • delay - in milliseconds
  • callback - a function that will be called after the delay ends

Let’s go back to our taxi example:

const callTaxi = (destination) => {
  console.log(`Your taxi to the ${destination} has arrived!`)
}

setTimeout (() => {
  callTaxi('airport')
}, 1000 * 60 * 60 * 5);

In the last line, we set the timeout to “in 5 hours”, after which the function passed as the first parameter will be executed. We specifically write the delay with a formula so that you understand the inner details. There is no way to call setTimeout with anything other than milliseconds, so any time value must be converted to ms.

In setTimeout, we must always pass a function as the first parameter. We couldn’t just say callTaxi('airport') because that statement would have been executed instantly and our plan would not have worked.

On the other hand, if we are sure that callTaxi will always be used with setTimeout, we can rewrite it and make it return a function.

const callTaxi = (destination) => () => {
  console.log(`Your taxi to the ${destination} has arrived!`)
}

setTimeout(callTaxi('airport'), 1000 * 60 * 60 * 5);

Now the code has become a little cleaner, callTaxi returns a function that will be called by setTimeout after 5 hours or 18,000,000 milliseconds.

Periodic execution and setInterval

Imagine that we are working on a top-secret facility and we need to check the security of some of its components at different intervals.

  • The perimeter should be checked every hour
  • Checkpoints should be checked every 3 hours
  • Digital systems should be checked daily

A timeout will not work for us here, because we need to run checks with a regular frequency, and not once. But, in addition to setTimeout, JS has another useful function - setInterval. It takes the same two parameters (callback, delay) and runs the callback function at intervals equal to delay.

const checkSecurity = (target) => () => {
  console.log (`Checking ${target}'s security ...`);
}

setInterval(checkSecurity('perimeter'), 1000 * 60 * 60);
setInterval(checkSecurity('checkpoint'), 1000 * 60 * 60 * 3);
setInterval(checkSecurity('digital systems'), 1000 * 60 * 60 * 24);

The checkSecurity function returns a function, which can be used in setInterval. When it’s called, it will perform a security check.

Next, we create three intervals, each of which will run until the program in which they were started is stopped. The intervals are started with a different delay parameter, so each of them will run at its own frequency.

To test the functionality and not wait for hours, you can run this code and remove 60 * 60, so the perimeter check will occur once a second, not once an hour.

How to delete sсheduled timeout or interval

Sometimes, it can be useful to remove a timeout or interval that’s already been set. For example, we set the alarm for “in 8 hours” and go to bed. We wake up after 7 hours and don’t need an alarm anymore.

We need to somehow ask the computer to remove the scheduled execution and clear the timeout.

Fortunately, this functionality is already implemented in JS. The setTimeout function returns the id of the timeout after we create it. And the global function clearTimeout(id), removes the scheduled execution from the queue.

const alarm = () => {
  console.log('Bzzz, Bzzz!! WAKE UP!')
}

const timeoutAlarm = setTimeout(alarm, 1000 * 60 * 60 * 8);

if (isAwake) {
  clearTimeout(timeoutAlarm);
}

If isAwake is equal to true, the timeout will be cleared and the alarm will no longer be active.

The same works for setInterval. But this time you need to use the clearInterval(id) function.

let i = 0;
const printWelcomeMessage = () => {
  if (i > 9) {
    clearInterval(printNumbersInterval);
  }
  console.log('Hey, we learn JS!');
  i++;
}
const printNumbersInterval = setInterval(printWelcomeMessage, 1000);

This example is a little more complicated, but the essence remains the same. We stored the interval id in printNumbersInterval and when we decided that we no longer need to run the printWelcomeMessage function, we passed this id to clearInterval.

You can try to run this code and make sure that the message Hey, we learn JS! is displayed only 10 times, after which the program exits.

Note that we used closure again when we declared the variable i outside of the printWelcomeMessage function. If we were to move let i = 0 inside the function, there would be problems.

FAQ

— What problems with moving the declaration of the counter let i = 0 should I expect?
— There is a practical task for this. Try to guess the value of the counter each time we check it in the if statement.