What's with setInterval?
Posted on November 17, 2018 -  đŠđźâđťÂ 5 min readIt all started in Chrome Dev Summit 2018 at the talk Paul & Surma gave about the Actor Model and how it can be applied in web development. Somewhere in the middle of the talk Paul blurbs âI hate setInterval, donât use it, just use setTimeout instead. If you want to know why come see me after the talkâ At that point I couldnât listen to the talk anymore and was just thinking âWHYâŚ?!â
Just kidding, the talk is really interesting and you should check it out!
After the talk
Well, I took Paul on his word, I reached out to him after the talk because I was still hung up on âWHYâŚ?!â so, I ask him âYouâve probably been asked that a lot by now, but why do you hate setInterval?â. He replied with âActually youâre the first one. Now think of it this way, your current task might be too long that itâll queue your callback more than onceâ It was so obvious now! well, actually not, it took me a couple of more sentences to actually get that, but I got there eventually đ
The hate đ
so letâs try and understand together why this small little nuance was bothering him so much. What does it mean âyour current task is too longâ? We all should know by now to keep all of our work under 16ms, that way the browser can do all the render pipeline (style, layout, paint, composite) to keep the frame rate at 60fps so our app will be responsive. Unfortunately, thatâs not always the case. Sometimes we go overboard, itâs not always bad, if youâre not animating, doing the work in 100ms, 150ms is fine, the user wonât notice anything Letâs say your work takes 150ms, which is still within the reasonable range, and at the start of that work you have
setInterval(() => {
...
}, 50);
whatâs so wrong with that you ask? apart from not saving the interval id to later cancel it⌠Not sure? thatâs good, it means this hasnât fully sunk in for you and I get to tell you! As we all know by now, JavaScript is single threaded (I know I know, the scheduler and task proposals that were announced the same day, letâs ignore them as theyâre still proposals), and by being single threaded it means that no matter what you do you canât make the browser stop the current thread, do some other work and come back like in other languages. In JavaScript, when you write
const a = 1;
const b = 2;
...
const c = a + b;
No matter what (unless an error is thrown) thereâs no code running between the first two statements and the last one other than the ...
.
This doesnât mean that the browser doesnât know how long the entire thing took, oh it knows alright.
If youâll call setInterval with, letâs say 50ms, and afterwards your current task will continue to run for 150ms, the browser knows, but it canât stop. What does it do in this situation? add more calls! You see, it doesnât necessarily have to run your code at exactly every 50ms, itâll run it at the time closest to 50ms and 100ms and 150ms and 200ms from now. Where now is the time setInterval was called. Letâs go back then, what is the closet time to 50ms from now the browser can run the code? after 150ms when the current task finishes. OK, and what is the closest time to 100ms from now the browser can run the code? after 150ms when the current task finishes and after the next iteration of setInterval. Same thing for when we ask about 150ms from now. Well, the browser just called our callback 3 times almost immediately after we finished the current task, just because it wants to be a good guy and make sure you get the calls you asked for! Thisâll exacerbated even further if each of your setInterval callbacks takes more than 50ms which means theyâll just pile up and youâll end up playing catch-up trying to figure out what went so horribly wrong.
The alternative
Paul also said in the talk to use setTimeout instead, letâs see why. The implementation is pretty known and simple, we just register another setTimeout callback within our original setTimeout callback. Something like this
const fakeSetIntervalCall = () => {
setTimeout(() => {
// do some work
...
fakeSetIntervalCall();
}, 50);
};
Now, thereâs a loop of setTimeout calls that happens in intervals of around 50ms. You may ask yourself, âWHY⌠is that any different?!â Because, for setTimeout calls the browser asks just once when is the closest time to 50ms from now I can run this code? after 150ms when the current task finishes. And thatâs it. Thatâs all the callbacks the browser queues, because it doesnât execute the inner code just yet to queue the second time. You just ensured your callbacks are executed in a distance of at least 50ms even if it took you more than that time to just execute a single task.
The â¤ď¸
This post was inspired by a super lightning fast chat I had with Paul that made me go âah, thatâs interestingâ while he just casually explains it in a couple of sentences and says âI got to get my food now, nice meeting you!â so thank you Paul for taking the time to talk to me about this and Iâm sorry I made you wait a little while longer for you to get your food!