Javascript - Why does measuring time becomes inaccurate in a iterating function? - javascript

Consider this piece of code
// Initialise the variables to check when the code started
let total_time = performance.now();
let recursions = 0;
function test() {
recursions++;
if (recursions === 60) {
// Check how long it took to do 60 recursions
console.log(`${performance.now() - total_time}ms`);
}
// Timeout to call it 17ms later
setTimeout(() => test(), 17);
}
test();
This is meant to show when 60 recursions have happened to ensure that certain parts of my code are fired off correctly (without needing setTimeout as it's blocking) and I get between 1200 - 1600ms for no reason. I used a modified version that measured how long it took and it gave 0ms (for no reason). Anyone know why? And will the same happen in node.js?
Code used to measure how long it took to do one recursion
let start_time = performance.now();
let total_time = performance.now();
let measure = 0;
function test() {
start_time = performance.now();
measure++;
if (measure === 60) {
console.log(`${performance.now() - total_time}ms`);
}
console.log(`Taken ${performance.now() - start_time}`);
}
test();

It's not supposed to be very accurate as you might expect since the timeout can fire an event later when the page is busy with other tasks.
setTimeout is implemented in a way it's meant to execute after a minimum given delay, and once the browser's thread is free to execute it. so, for an example, if you specify a value of 0 for the delay parameter and you think it will execute "immediately", it won't. it will, more accurately, run in the next event cycle (which is part of the event loop - concurrency model which is responsible for executing the code).
So for conclusion, you can't use setTimeout if you expect a consistent, reliable
and accurate timing in scale of millisecond.
Please read reason for delays longer then specified - https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#reasons_for_delays_longer_than_specified
More about the JS event loop - https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop

Related

Making a clock that takes 2560ms to complete a revolution in JavaScript

I'm trying to make a clock for a Libet task - a cognitive task used by psychologists. By convention these clocks take 2560 ms to complete a revolution. Mine seems to be running quite a lot slower and I can figure out why.
I have made an example of the issue here: https://jsfiddle.net/Sumoza/re4v7Lcj/8/
On each iteration of a setInterval with a 1ms delay, I increase the angle of rotation of the hand by (Math.PI*2)/2560, i.e. 1/2560th of a circle in radians. So, incrementing one of these each ms should complete the circle in that number of ms. As you can see from the clock, this runs a fair bit slower. Can anyone shed any light as to why. Interestingly, *10 seems to look a lot closer to what I want - but that doesn't make much sense to me so I'm wary of the fix. Relevant JS from the fiddle below. Thanks for any help.
var time = 0;
var rad_tick = (Math.PI*2)/2560; //radians per tick: last number is ms per revolution
// Draw Clock
const clock = document.getElementById('clock')
const clock_ctx = clock.getContext('2d')
clock_ctx.translate(clock.width/2, clock.height/2);
radius = (clock.height/2) * 0.7;
function drawClock() {
clock_ctx.clearRect(-clock.width/2, -clock.height/2, clock.width, clock.height);
clock.style.backgroundColor = 'transparent';
clock_ctx.beginPath();
clock_ctx.arc(0, 0, radius, 0 , 2 * Math.PI);
clock_ctx.stroke();
}
drawClock();
// Draw Hand
const hand = document.getElementById('hand')
const hand_ctx = hand.getContext('2d')
hand_ctx.translate(hand.width/2, hand.height/2);
function drawHand(time){
hand_ctx.clearRect(-hand.width/2, -hand.height/2, hand.width, hand.height);
hand_ctx.beginPath();
hand_ctx.moveTo(0,0);
hand_ctx.rotate(time);
hand_ctx.lineTo(0, -radius*0.9);
hand_ctx.stroke();
hand_ctx.rotate(-time);
}
function drawTime(){
time+=rad_tick;//*10
drawHand(time)
}
var intervalId = setInterval(drawTime, 1);
Try this
var starttime = new Date().getTime();
function drawTime() {
var elapsed = new Date().getTime() - starttime;
var time = elapsed % 2560;
time = time * rad_tick;
console.log(elapsed + "->" + time);
drawHand(time)
}
Base things on the current time, and don't depend on setInterval to fire at the exact time you set it to fire. If there are things in-queue at the time that setInterval is supposed to fire, it will do those things in-queue first and only then run the function you've wired to setIterval (causing the delays you see --which build up more and more over time). Learn about the event loop and requestAnimationFrame.
Instead, get the time (freshly) prior to doing something, and calculate how much time has passed since the start time. Base your animations on these calculations. This way, even if there are slight delays, your last action will always sync things to the way they're supposed to look right now.
When you do it this way, you might lose some frames (skipping things the engine didn't have time to do), but it will likely complete much closer to the expected completion time.
The bottom line is, if you tell the browser to do more than it can do in a particular amount of time, there will be delays. However, you can skip frames of an animation to make things complete much closer to the expected completion time.
You ask setInterval to call your callback every 1ms, but does it actually call it every 1ms?
Try this code:
let cnt = 0;
setInterval(() => cnt++, 1);
setTimeout(() => { console.log(cnt); }, 1000);
For me, it prints something between 230 and 235. So looks like the actual (average) minimum interval for me is a bit over 4ms.
Adding on to Stig, since javascript isn't super fast when it comes to these things, it won't be super accurate. Using something like getTime() will allow proper timing. How about you call getTime in your drawTime function, so that around every millisecond, it will check the actual time and use that value.

Recursive function call with setTimeout limited to ~200 calls per second, why?

This code recursively calls the same function with a setTimeout of 1 millisecond, which in theory should call the function 1000 times per second. However, it's only called about 200 times per second:
This behavior happens on different machines and different browsers, I checked if it's has something to do with the maximum call stack, but this limit is actually way higher than 200 on any browser.
const info = document.querySelector("#info");
let start = performance.now();
let iterations = 0;
function run() {
if (performance.now() - start > 1000) {
info.innerText = `${iterations} function calls per second`;
start = performance.now();
iterations = 0;
}
iterations++;
setTimeout(run, 1);
}
run();
<div id="info"></div>
There’s a limitation of how often nested timers can run. The HTML5 standard says: 11: "If nesting level is greater than 5, and timeout is less than 4, then set timeout to 4."
This is true only for client-side engines (browsers). In Node.JS this limitation does not exist
HTML Standard Timers Section
Very similar example from javascript.info
The delay argument passed to setTimeout and setInterval is not a guaranteed amount of time. It's the minimum amount of time you could expect to wait before the callback function is executed. It doesn't matter how much of a delay you've asked for, if the JavaScript call stack is busy, then anything in the event queue will have to wait.
Also, there is an absolute minimum amount of time you could reasonably expect a callback to be called after which is dependent on the internals of the client.
From the HTML5 Spec:
This API does not guarantee that timers will run exactly on schedule.
Delays due to CPU load, other tasks, etc, are to be expected.
I once read somewhere that it was around 16ms so setting a delay of anything less than that shouldn't really change the timings at all.

setTimeout() triggers a little bit earlier than expected

UPD: The question What is the reason JavaScript setTimeout is so inaccurate? asks why the timers in JavaScript are inaccurate in general and all mentions of the inaccuracy are about invocations slightly after the specified delay. Here I'm asking why NodeJS tolerates also invocations even before the delay? Isn't it an error-prone design of the timers?
Just found an unexpected (to me only?) behaviour of NodeJS setTimeout(). Some times it triggers earlier than the specified delay.
function main() {
let count = 100;
while (count--) {
const start = process.hrtime();
const delay = Math.floor(Math.random() * 1000);
setTimeout(() => {
const end = process.hrtime(start);
const elapsed = (end[0] * 1000 + end[1]/1e6);
const dt = elapsed - delay;
if (dt < 0) {
console.log('triggered before delay', delay, dt);
}
}, delay);
}
}
main();
On my laptop output is:
$ node --version
$ v8.7.0
$ node test.js
triggered before delay 73 -0.156439000000006
triggered before delay 364 -0.028260999999986325
triggered before delay 408 -0.1185689999999795
triggered before delay 598 -0.19596799999999348
triggered before delay 750 -0.351709000000028
Is it a "feature" of the event loop? I always thought that it must be triggered at least after delay ms.
From the NodeJS docs:
The callback will likely not be invoked in precisely delay milliseconds. Node.js makes no guarantees about the exact timing of when callbacks will fire, nor of their ordering. The callback will be called as close as possible to the time specified.
As you increase the number of intervals (you have 100) the accuracy decreases, e.g., with 1000 intervals accuracy is even worse. With ten it's much better. As NodeJS has to track more intervals its accuracy will decrease.
We can posit the algorithm has a "reasonable delta" that determines final accuracy, and it does not include checking to make sure it's after the specified interval. That said, it's easy enough to find out with some digging in the source.
See also How is setTimeout implemented in node.js, which includes more details, and preliminary source investigation seems to confirm both this, and the above.
From node timers doc, about settimeout:
The only guarantee is that the timeout will not execute sooner than the declared timeout interval

Javascript setTimeout timing reliability

I have recently started exploring Javascript in more detail, and how it executes within the browser. Specifically, the setTimeout function.
My understanding is that calling setTimeout(foo,x)
will pass a handle to foo to be executed after x milliseconds. How reliable is this timing? Obviously if another long-running script is still executing after x milliseconds then the browser won't be able to call foo, but can I be absolutely certain that setTimeout(foo,101) will always be executed after setTimeout(foo,100)?
First of all, the timeout is in miliseconds, therefor 1 sec = 1000 ms. consider that.
you can always be sure that delay of 1001 will be later than 1000.
BUT You must remember that if the 2nd methods relay on changes of the first method it doesnt mean it will work good.
the first methods can take for reasonable time of 3ms (not a complicated one) and the 2nd one can start only 1 ms after the first one causing your reliability on the first method to fail.
i would suggest not to use this feature but in some rare cases.
you can tag me in this answer comment for your specific case and i can suggest the right way to work it out.
In nodejs we may be able to time events very precisely by setting a timeout to expire shortly before the desired moment, and then creating a tight series of event-loop ticks, and after each one checking how close to the target time we've approached:
Imagine Date.now() is currently, e.g., 11000 (unrealistic; just an example!)
Determine we want to act in EXACTLY 4000ms
Note that means EXACTLY when Date.now() === 15000
Use setTimeout to wait less than 4000ms, e.g., 3800ms
Keep awaiting microticks until Date.now() >= 15000
This won't block the event loop
(But it will keep your CPU very busy)
let preciseWaitMs = async (ms, { nowFn=Date.now, stopShortMs=200, epsilonMs=0 }={}) => {
let target = nowFn() + ms;
await new Promise(resolve => setTimeout(resolve, ms - stopShortMs));
// Allow `epsilonMs` to shift our wait time by a small amount
target += epsilonMs;
// Await a huge series of microticks
// Note: this will eat cpu! Don't set `stopShortMs` too high!
while (target > nowFn()) await Promise.resolve();
};
(async () => {
let t1 = Date.now();
let target = t1 + 2000;
console.log(`Trying to act in EXACTLY 2000ms; at that point the time should be ${target}`);
await preciseWaitMs(2000);
let t2 = Date.now();
console.log(`Done waiting; time is ${t2}; deviation from target: ${t2 - target}ms (negative means early)`);
})();
Note that preciseWaitMs(4000) will never wait less than 4000ms. This means that, if anything, it is biased towards waiting too long. I added an epsilonMs option to allow the bias to be moved back according to the user; for example preciseWaitMs(4000, { epsilonMs: -1 }) may cancel out preciseWaitMs's bias towards always being late.
Note that some js environments provide a higher-precision current-time query than Date.now - for example, nodejs has require('perf_hooks').performance.now. You can supply a function like this using the nowFn option:
{ epsilonMs: -0.01, nowFn: require('perf_hooks').performance.now };
Many browsers support window.performance.now; try supplying:
{ epsilonMs: -0.01, nowFn: window.performance.now };
These settings achieve sub-millisecond "precise" timing; off by only about 0.01ms on average
Note the -0.01 value for epsilonMs is what seemed to work best for my conditions. Note that supplying fractional epsilonMs values is only meaningful if nowFn provides hi-res timestamps (which isn't, for example, the case with Date.now).
Most browsers use single thread for UI and JavaScript, which is blocked by synchronous calls. So, JavaScript execution blocks the rendering.
Events are processed asynchronously with the exception of DOM events.
but setTimeout(function(),1000) trick is very useful. It allows to:
Let the browser render current changes. Evade the “script is running too long” warning.
Change the execution flow. Opera is special in many places when it comes to timeouts and threading.
So if another function is executing it will handle it by running in parallel.
another thing to setTimeout(function(),1000) her time is in millisecond not in seconds.

Javascript countdown is going slow?

I'm trying to make a countdown that is counting down in milliseconds; however, the countdown actually takes much longer than 7 seconds. Any idea as to why?
function countDown(time){
var i = 0;
var interval = setInterval(function(){
i++;
if(i > time){
clearInterval(interval);
}else{
//mining
$('#mining_time').text($('#mining_time').text()-1);
}
}, 1);
}
And I can confirm the varible time passed to the function is correctly set to 7000.
For a mostly-accurate countdown, use setTimeout().
setTimeout(fn, 7e3);
If you absolutely must have it as close to 7 seconds as possible, use a tight poll (requestAnimationFrame()) and look at difference between the time of start and current poll.
var startTime = Date.now();
requestAnimationFrame(function me() {
var deltaTime = Date.now() - startTime;
if (deltaTime >= 7e3) {
fn();
} else {
requestAnimationFrame(me);
}
});
Poly-fill as required.
the most precise way to run something after 7 seconds - is to use setTimeout with 7000 ms interval
a. there is no browser that guarantees an interval to run with 1ms resolution. In the best case it would be 7-10ms
b. there is only one thread in js, so the tasks are queued. It means that the next run will be scheduled to only after the current run is finished.
Some useful reading: http://ejohn.org/blog/how-javascript-timers-work/
No browser will take 1 as parameter for setInterval. Off the top of my head the minimum is 4 ms.
For an accurate result, get the current time, add 7000 ms, and poll (using setInterval or setTimeout) until you reach that new time.
A quick Web search returned this article that provides an example.
[Update] the value of 4 ms is mentioned on this MDN page.

Categories