JavaScript counters the hard way - HTTP 203

Chrome for Developers · 43K views · 2021 · 24:08

Open on YouTube

Web developmentSoftware performance & optimizationtalk

Why Stack Overflow recommends it

Stack Overflow users link it when answering questions about setInterval drift, timers that misbehave in background tabs, and how to build an accurate, efficient counter or clock.

Recommended by 7 people across 7 different months, 2021 to 2022.

What it is

An episode of Chrome's HTTP 203 series in which Jake Archibald and Surma pick apart common JavaScript counter and timer implementations. They compare setInterval, setTimeout and requestAnimationFrame approaches and arrive at a reusable solution that stays accurate and efficient across browsers.

What people said

Strong rec charlietfl · 2021-04 · Stack Overflow · on “Idle games and web workers for background tabs”

This video [this video] is just what you want and this gist with workable solution [LINK] from same video. Worth watching. Very informative

Recommends Theraot · 2021-03 · Stack Overflow · on “setInterval function not working properly when window/browser tab is minimized”

Recommended watching: JavaScript counters the hard way - HTTP 203 [this video] . You might also be interested in Scheduling Tasks - HTTP 203 [LINK] .

Recommends Sarah Groß · 2021-05 · Stack Overflow · on “A timer where user enters variable for future date”

Here's a solution using a date input [LINK] , which is more semantic [LINK] , accessible [LINK] and more comfortable to use [LINK] for the end user. It's using inline styles to switch between the form and the countdown, but the same could be achieved using classList [LINK] . toggle() [LINK] and a class for showing or hiding the specific view. You should also implement some form of error handling (e.g. right now it simply does nothing when clicking the button while not having set a date, and it also supports dates in the past, resulting in negative numbers). A note on setInterval() : It's not reliable/exact enough and may result in seconds being skipped or changing too quickly. For more backg

Recommends Jacob · 2022-05 · Stack Overflow · on “My counter stop when i switch tab (reactjs)”

This might not answer the question but I recommend watching this presentation about counters [this video] . It shows the different browser quirks and issues with `setInterval`, and how to fix them.

Recommends Sysix · 2022-06 · Stack Overflow · on “setTimeout callbacks have different execution orders in Firefox and Chrome”

I can't give you an full detailed explanation, but the second paramter of setTimeoput and setInterval doesn't mean, it will exactly execute it at that time. They will put it in a queue, so the background can execute it. The browser has a lifecycle when to execute specific steps to update the data and the styles. I can only send you this youtube link, that helped me to learn more about it: [this video]

Recommends brunnerh · 2022-07 · Stack Overflow · on “I have Code to get date and time then display it but how do I make it refresh itself in svelte”

You would have to re-assign the variables periodically, e.g. using setInterval , using a new Date each time. Note, that if you use exactly one second as an interval, changes could be missed due to shift/slight delays. Implementing an accurate and efficient timer is an interesting problem of its own, there is an entire HTTP 203 episode on that [this video] . Jake's implementation is this (made signal optional): export function animationInterval(ms, signal, callback) { // Prefer currentTime, as it'll better sync animtions queued in the // same frame, but if it isn't supported, performance.now() is fine. const start = document.timeline ? document.timeline.currentTime : performance.now(); function fr

Recommends jsejcksn · 2022-10 · Stack Overflow · on “Is there a way to alternate setInterval durations?”

JS timers have a very complicated history. Using a recursive setTimeout invocation is a simple and elegant solution as long (as your runtime implements tail call optimization [LINK] ). Separate from the issue of recursion is the issue of timer drift. This is covered in the YouTube video JavaScript counters the hard way - HTTP 203 [this video] if you'd like an accessible introduction. In many JS engines (e.g. V8) setInterval [LINK] will handle drift correction for you, so there's actually an advantage to using it over recursively invoking setTimeout . (Check the millisecond timestamps in the console messages in the snippet below to verify this.) In order to determine the constant interval argument

Cites Grzegorz T. · 2021-02 · Stack Overflow · on “Using vanilla JavaScript, is it possible to add a DOM element and fade in using CSS transition, without using setTimeout or CSS animation?”

That's right, it's more complicated to use `requestAnimationFrame`. Such a curiosity from `setTimeout()` take a look - JavaScript counters the hard way - HTTP 203 [this video]

Cites nonopolarity · 2021-03 · Stack Overflow · on “In Google Chrome or other browsers, can requestAnimationFrame() be able to be pushed to higher repaint frequency?”

"the JS engine tells the DOM to update synchronously" are you saying this happens in the context of `requestAnimationFrame(fn)`? If `fn` is supposed to be called at 010/1000 and 026/1000 of the second, and you update the DOM tree at 012/1000 and 017/1000 and then 021/1000 and then 025/1000, so isn't the 012/1000 and 017/1000 and 021/1000 update wasted? I guess if you don't worry about CPU work wasted and electricity wasted, it is not an issue. This discussed wasted effort: [this video]

Cites Theraot · 2021-04 · Stack Overflow · on “How to use promise with set interval”

I have adapted this gist [LINK] by Jake Archibald (see JavaScript counters the hard way - HTTP 203 [this video] ) into the following code: function promiseInterval(milliseconds, signal, promiseFactory, callback) { const start = performance.now(); function tick(time) { if (signal.aborted){ return; } promiseFactory().then( value => { callback(value); scheduleTick(time); } ); } function scheduleTick(time) { const elapsed = time - start; const roundedElapsed = Math.round(elapsed / milliseconds) * milliseconds; const targetNext = start + roundedElapsed + milliseconds; const delay = targetNext - performance.now(); setTimeout(tick, delay); } scheduleTick(start); } Starting from the gist, I have removed