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 millisecondscallback
- 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.