JavaScript SetInterval() — A google-free solution for consistent timing

Chris Hodge
4 min readFeb 25, 2020

--

In coding when I come across a challenge that after some analysis I cannot work out an immediate solution for or in a situation where the are several clear options to solve it but it is not obvious which one would be the best choice, I will of course Google it. This often tends to help find a solution to the problem, because even if you can’t find a perfect solution, you will get incites and inspiration into possible way to solve it.

Now as I said most of the time Google-ing is a big help, however recently I had a challenge to which Google wasn’t very helpful…

For my site Carbon Forecast (which is still in development — www.carbonforecast.net). I have a chart that shows the user a forecast of the mix of fuels that go into producing the electricity supply to their home. It shows the mix per hour as a pie chart allowing the user to step through each hour. I thought it would be a nice feature for the chart to step through each hour automatically so the user could what the transitions and changes.

carbonforecast.net fuel mix player

As I’m using React and a counter in state to monitor which hour from the dataset the chart is displaying, it seemed to me that the obvious solution was to write a method that incremented the counter with a setInterval() callback function, to delay the change. I was aware of difficulties of using setInterval() (or setTimeout), due to Javascript being a single thread language…

JavaScript’s is a single thread concurrent language, meaning it can handle one task at any one time. This is managed with the call or execution stack, which is a list of all the functions to be executed. The problem is that the time interval used in setInterval() is just the amount of time javascript waits before attempting to start executing the function not the exact time when the function will complete, the function will require time to complete itself. Additionally any other functions in the execution stack may delay the completion of the setInterval().

This this means that in my case where I wanted a nice even delay between increasing the count, problems arise. Instead of a nice even change between the data for each hour I got a varying uneven change sometimes the hours would change in quick succession sometimes with a longer delay. Not what I wanted.

So as I ever I turned to Google for advice, however this time the results looked barren. It seemed logical to me that there must be some straightforward way to do this, someone else must have wanted to do this??? But I really couldn’t find anything of use that would work with vanilla JavaScript or React. — Now it is very possible that my Google-ing was off and I was searching for the wrong things, I’m still convinced there must be a straightforward way to do this… (I look forward to the comments :D) — However I couldn’t find anything so I had to turn to my own cunning and brawn.

After quite a bit of experimenting this is the solution I came up with that works. It’s somewhat long winded but here we go…

Below is the code I came up with. The key to it is to use a variable for the interval time value, rather than specifying say 800 milliseconds for every loop. Using a variable lets you alter the time to delay each time. My thinking was that if I can workout how far form my desired time interval (in my case 800milliseconds) the execution actually occurred I could alter the forthcoming time interval values accordingly so that the count change didn’t seem to get too far ahead or behind.

Therefore to do this I needed to find the time difference between every increment of the counter. As I was using this to control a counter I differentiated two sequential counts, by checking if a number was odd or even.

player = () => {let counter = 0
let timeA = Date.now()
let timeB = Date.now()
const interval = 800
let intervalRunner = setInterval(() => {
if (this.state.playing) {
this.handleCount(+1)
counter%2 === 0 ? timeA = Date.now() : timeB = Date.now()
let check = 0
counter%2 === 0 ? check = (timeA-=timeB) : check = (timeB-=timeA)
check >= 1 ? interval = (800 - check) : interval = (800 + check)
counter += 1
} else clearInterval(intervalRunner)

},interval >= 1 ? interval : 0)
}

Using the Date.now() I had a reference for the time at which an execution occurred. Then I subtract the greater value from the lesser. Next I compare this to 800 milliseconds and get how far ahead or behind it is. Then if it is ahead (fast) I add how far ahead it is to 800, and if it behind (slow) subtract this value from 800. This final value is the time interval used for the setInterval().

The result was what I wanted, the chart stepped automatically through each hour with a smooth steady pace!

I have to say I rather enjoyed the process of figuring out a solution to this, occasionally I think it might be nice to live in a world without Google…

--

--

Chris Hodge

Fledgeling software engineer, former consultant. Bootcamp graduate. Likes creative projects, has creative energy. Passions: full stack dev, music, green energy.