If we have to use timers, it's best to use setTimeout
instead of setInterval
.
This way the memory consumption stays low, and browser tabs won't freeze much overtime.
However there is an issue: the loops are not well cadenced, the next loop does run after all tasks completed. See the time loops are not equal and are increasing, little by little:
const timer = setTimeout(function tick(){
console.log("Hi!")
// <-- More jobs here -->
setTimeout(tick, 2000);
}, 2000);
We wouldn't try to create some music in this manner.
A proper more modern way is to NOT use interval
or timeout
at all, but instead to continuously check the date epoch and proceed accordingly.
To do so, we can use requestAnimationFrame
.
In this example, the next loop will be triggered AFTER all functions complete.
This does replace setInterval
very well and is more flexible, however this is still not optimal regarding the cadence, after many loops, the intervals are still shifting!!!
/* Countdown loop ----------------------------------------*/
let job = null, origin = new Date().getTime()
const timer = () => {
if (new Date().getTime() - 2000 > origin){
console.log("Hi!")
origin = new Date().getTime()
job = requestAnimationFrame(timer)
} else {
requestAnimationFrame(timer)
}
}
/* Start looping -----------------------------------------*/
requestAnimationFrame(timer)
To completely solve both issues:
The loops are well cadenced, and we don't bother with setInterval
any more. Every devices will adapt, they will just slow down their animationFrame
rate at their own cadence, but still be on time.
/* Timer loop ----------------------------------------*/
let job, origin = new Date().getTime(), i = 0;
const timer = () => {
if (new Date().getTime() - i > origin){
console.log("Hi!")
i = i + 2000
job = requestAnimationFrame(timer)
} else if (job !== null){
requestAnimationFrame(timer)
}
}
/* Start looping or start again ------------------------*/
requestAnimationFrame(timer)
// Stop the loop
// job = null