
A tour of JavaScript timers on the web - octosphere
https://nolanlawson.com/2018/09/01/a-tour-of-javascript-timers-on-the-web/
======
jonquark
From the title, I thought this was going to be a collection of Javascript
based count down timers like:
[http://coralbark.net/javascriptTimer/](http://coralbark.net/javascriptTimer/)

(It describes ways of timing things in Javascript code).

~~~
decasia
Ha, I independently wrote almost the identical thing (minus bells and
whistles) on Friday in a fit of procrastination.

So if anyone wants a dark mode for their hacked together javascript timer:

[https://decasia.org/tools/timer.html](https://decasia.org/tools/timer.html)

------
Jarred
React Native adds an additional timer:
InteractionManager.runAfterInteractions[0]

It's useful for running code after an animation has completed, to make it
easier for animations to be smooth. Would be cool for something like this to
be added into JS proper (particularly if CSS or JS animations are not
hardware-accelerated)

[0]: [https://facebook.github.io/react-
native/docs/interactionmana...](https://facebook.github.io/react-
native/docs/interactionmanager)

------
hokus
I haven't benched it in a while but as far as I know setTimeout and
setInterval can have an extra delay depending on [what I call] browser pain.

I measure this delay to set the pace of computationally heavy tasks.

I also consider it a proof of concept why browsers should expose more of the
performance and capability stats to the web page. Maybe I don't want
translucent layers of css animations if it freezes the gui? At least, I think
I don't.

Also funny:

Because callbacks can trigger computationally heavy tasks that may trigger at
the same time I've morphed into the habit of just slapping the data into an
array and parsing it with a setTimeout used as a setInterval.

So, basically I went back to polling because of superior performance. (ha-ha)

------
Waterluvian
Maybe I'm wrong but I always thought settimeout was far simpler than this blog
goes on about. My understanding is that it puts a frame into the queue that
keeps getting ignored until _at least_ timeout ms has elapsed.

Therefore, settimeout of 0 is to basically place a frame into the queue, "do
this once the stack is empty and there's nothing in front of it in the queue."

They're just about placing frames into some place in the queue.

Is it not that simple?

~~~
jayflux
The timer basically runs in another thread, and once it’s finished it’s
callback/frame goes into the queue, it does not go into the queue before the
time you gave. The reason the time can’t be accurate is because other “frames”
could be ahead of it in the queue after the timer has finished.

~~~
asknthrow
I don't think this is correct. AFAIK they both run in the UI thread (which is
why running your timer in a webworker turns out to be more accurate as only
the callback is affected by queue length of scheduled calls in the UI thread).
Although this article
[https://www.html5rocks.com/en/tutorials/audio/scheduling](https://www.html5rocks.com/en/tutorials/audio/scheduling)
is about sheduling audio specifically, it is the go-to resource for accurate
web timing (and although not explicitly mentioned, the metronome is supposed
to run in a webworker).

------
jancsika
So let's say I have a web assembly binary squirreled away in a web worker or
AudioWorklet.

If that binary is supposed to have its own event loop, how do I achieve that?
Seems like I'd have to hook back into the JS event loop, at which point my
poor binary becomes subject to all the DOM blockers I was trying to avoid in
the first place... :(

~~~
Lerc
The communication between worker and Host script is through postmessage. So
yes you are just moving the event pump to a onmessage pump. If you have an
infinite loop in a worker it will run fine, you just can't ever tell it
anything, rendering it somewhat pointless.

What is really needed is the ability to preempt/resume javascript in workers.

You could fudge that with a preprocessor script that inserted, at judicious
points, polling of a pseudo-interrupt flag. You'd have to use a
SharedArrayBuffer which you can't have because it got disabled post-spectre.

Unfortunately, I fear any mechanism that works properly would let you do
things precisely and well and would consequently be blocked as a possible
avenue for spectre.

~~~
jancsika
The only possibility I see currently is to just slave the main loop to the
AudioWorklet processing callback and blindly hope that it is able to hit the
scheduling deadlines.

The current native app (say, in Linux) has both a scripting language and a DSP
"dataflowy" language that can run and interact in the same process. This means
the user can manually (or programmatically) turn on DSP processing
opportunistically. However, with the Web Audio idea above this means that the
software I'm porting _must_ be calculating its DSP graph from the AudioWorklet
callback at all times for the software to work _at all_. If I suspend audio
computation in the parent script time would no longer move forward. Or I'd
have to switch to having the DOM poll the worker-- feeding it the current
time-- which I'm just guessing would kill performance.

------
tzs
Which is the right one for this situation?

I want to sort a table with a large number of rows after a user clicks on a
column heading. This will take several seconds, so I want to change the column
header to a bold, red "Sorting...please wait" while the sort is taking place,
then change it back.

After changing the heading, I need to put off calling the sort function until
after the heading change is actually rendered for the user.

The animation one sounds promising...but from the description it sounds to me
like it runs just before rendering, and I want after rendering.

The idle one also sounds good in that it sounds like the rendering will be
done before it runs...but if other stuff is going on it may take a long time
for idle to actually happen.

What I've used in the past was setTimeout with a value of 0, because that's
what Stackoverflow said to do.

~~~
TheAceOfHearts
If the list is REALLY long you should consider sorting it from a web worker.

You might also want to consider implementing windowing or using a library,
that way you only render what's visible on-screen. However this has its own
caveats, so I'd suggest considering it carefully. For example, it'll break
browser search for non-visible items. If you can get away with it I'd
definitely suggest sticking with a traditional <table> element.

Can we get an idea of the size of this list?

~~~
tzs
This was several years ago, so there is some haziness in my memory.

There was an internal tool written in PHP that that put up a form for customer
support people to enter information about transactions they were interested
in, then queried the customer database to find matching transaction. It then
output a page with the filled-in search form and a table with the results.
There was no JavaScript used on that page. Just a simple form and the table.

It didn't happen often, but occasionally the table could have many thousands
of rows.

The project to add sorting was one of those "it would be nice if we could sort
this...I bet there's code on Stackoverflow that I can just grab and drop in
between things I'm actually supposed to be working on today!" things.

------
acemarke
FWIW, the React team _just_ published a new package called `schedule` this
evening, which acts as a polyfill for `requestIdleCallback` and adds a bunch
of additional scheduling capabilities. It's intended to be used with React and
its upcoming async rendering capabilities, but appears to be sufficiently
standalone to be reused.

\- Package:
[https://www.npmjs.com/package/schedule](https://www.npmjs.com/package/schedule)

\- Source:
[https://github.com/facebook/react/tree/master/packages/sched...](https://github.com/facebook/react/tree/master/packages/schedule)

------
dandare
I don't understand the purpose of setTimeout(/* ... */, 0) (or setImmediate).
If I want a function to execute immediately I will call it directly, not via
setTimeout(0). What am I missing?

~~~
Doxin
setTimeout(0) doesn't call the function immediately. It will call the function
at the next opportunity. Usually this means that for example that element you
added to the DOM has now rendered.

~~~
tzs
That does not appear to be the case in Firefox (62.0). Using dandare's blue
button adding function from his reply to your comment here is some test code:

    
    
        function myFunction()
        {
            var btn = document.createElement("BUTTON");
            var t = document.createTextNode("CLICK ME");
            btn.appendChild(t);
            document.body.appendChild(btn);
            btn.style.color = "blue";
        } 
    
        function slow(n)
        {
            console.log('slow called');
            let s = 0;
            for (let i = 0; i < n; ++i)
                s += 1.00000001;
            console.log('slow:' + s);
            return s;
        }
    
        function callMyFunction(to)
        {
            myFunction();
            setTimeout(function () {slow(5000000000);}, to);
        }
    

The argument to slow() in the timeout function, 5000000000, causes it to take
about 5 seconds on my system, long enough to easily tell if the button has
rendered before slow runs.

In Chrome, callMyFunction(0) results in the blue button showing up, then 'slow
called' in the log, then 5 seconds later slow() finishes. I did this several
times and the results were the same every time.

In Firefox, it results in 'slow called' in the log, then 5 seconds later slow
finishes and the blue button shows up. Several tries with callMyFunction(0)
resulted in no instances of the rendering being done before the timeout
function was called.

I had to bump the argument to callMyFunction up to 35 to get the rendering to
happen consistently before the timeout function (10 out of 10 times). It
mostly completed before the timeout at 34, but not always.

This is surprising to me, because the impression I got when I looked into this
a few years ago on Stackoverflow and other web sites was the setTimeout(0) was
a reliable way to make sure that all DOM changes were rendered.

My test procedure: open browser. Open inspector. Paste the above code into the
console. invoke callMyFunction from the console while watching the screen for
the button and the log for slow's messages. I wonder if JavaScript invoked
from the console might have some different rules for rendering timing?

Edit: nope, it has nothing to do with the console. I tried the above, but
invoked by a click on a page instead of from the console, and the results are
the same.

