Is there anything faster than setTimeout and requestAnimationFrame? - javascript

(I need a process.nextTick equivalent on browser.)
I'm trying to get the most out of javascript performance so I made a simple counter ...
In a second I make continuous calls to a function that just adds one to a variable.
The code: codepen.io/rafaelcastrocouto/pen/gDFxt
I got about 250 with setTimeout and 70 with requestAnimationFrame in google chrome / win7.
I know requestAnimationFrame goes with screen refresh rate so, how can we make this faster?
PS: I'm aware of asm.js

Well, there's setImmediate() which runs the code immediately, ie as you'd expect to get with setTimeout(0).
The difference is that setTimeout(0) doesn't actually run immediately; setTimeout is "clamped" to a minimum wait time (4ms), which is why you're only getting a count of 250 in your test program. setImmediate() really does run immediately, so your counter test will be orders of magnitude higher using it.
However you may want to check browser support for setImmediate -- it's not available yet in all browsers. (you can use setTimeout(0) as a fallback of course though, but then you're back to the minimum wait time it imposes).
postMessage() is also an option, and can achieve much the same results, although it's a more complex API as it's intended for more doing a lot more than just a simple call loop. Plus there are other considerations to think of when using it (see the linked MDN article for more).
The MDN site also mentions a polyfill library for setImmediate which uses postMessage and other techniques to add setImmediate into browsers that don't support it yet.
With requestAnimationFrame(), you ought to get 60 for your test program, since that's the standard number of frames per second. If you're getting more than that, then your program is probably running for more than an exact second.
You'll never get a high figure in your count test using it, because it only fires 60 times a second (or fewer if the hardware refresh frame-rate is lower for some reason), but if your task involves an update to the display then that's all you need, so you can use requestAnimationFrame() to limit the number of times it's called, and thus free up resources for other tasks in your program.
This is why requestAnimationFrame() exists. If all you care about is getting your code to run as often as possible then don't use requestAnimationFrame(); use setTimeout or setImmediate instead. But that's not necessarily the best thing for performance, because it will eat up the processor power that the browser needs for other tasks.
Ultimately, performance isn't just about getting something to run the maximum number of times; it's about making the user experience as smooth as possible. And that often means imposing limits on your call loops.

Shortest possible delay while still being asynchronous is from MutationObserver but it is so short that if you just keep calling it, the UI will never have chance to update.
So trick would be to use MutationObserver to increment value while using requestAnimationFrame once in a while to update UI but that is not allowed.
See http://jsfiddle.net/6TZ9J/1/
var div = document.createElement("div");
var count = 0;
var cur = true;
var now = Date.now();
var observer = new MutationObserver(function () {
count++;
if (Date.now() - now > 1000) {
document.getElementById("count").textContent = count;
} else {
change();
}
});
observer.observe(div, {
attributes: true,
childList: true,
characterData: true
});
function change() {
cur = !cur;
div.setAttribute("class", cur);
}
change();

Use postMessage() as described in this blog.

Related

How to make a precise sleep function in Javascript, possibly using promises?

I am trying to make a sleep function in Javascript.
The function drawLinesToHtmlCanvas() is meant to draw random lines to a HTML canvas and the user is meant to be able to see the lines being drawn in real time.
For this example I use a delay of 500ms, but would like to be able to go to 1ms (or even less than 1ms resolution in the future)
Originally I followed a answer from this post: What is the JavaScript version of sleep()?
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function drawLinesToHtmlCanvas() {
// Get canvas and context here...
var drawSpeed = 500; // ms.
for (i=0; i<lines; i++) {
// Draw lines to canvas...
await sleep(drawSpeed);
}
}
And that worked very well (above). It was efficient, did not slow the browser down at all and let me have some control over the timing.
The issue was that setTimeout() cannot seem to go down to 1ms precision and this is something I require for this function.
So instead I tried my own approach as follows:
function sleep(ms) {
ms = parseInt(ms);
var now = new Date();
nowMs = now.valueOf();
var endMs = nowMs + ms;
while (endMs > nowMs) {
nowMs = new Date().valueOf();
}
return true;
}
function drawLinesToHtmlCanvas() {
// Get canvas and context here...
var drawSpeed = 500; // ms.
for (i=0; i<lines; i++) {
// Draw lines to canvas...
while (!sleep(drawSpeed));
}
}
This one is very slow, the while loop waiting for the right time uses up all the browsers resources, it's completely unusable. In addition as the function drawLinesToHtmlCanvas() is running, the lines are not being updated to the canvas element.
The promise solution with the setTimeout() was fantastic, it is just not precise enough for my requirements.
Can I make a promise that works similar to the first example? But instead of using setTimeout() it uses a similar algorithm to my Date() now vs end ms comparison, as that would be much more accurate?
The lines need to be able to be drawn down to 1 ms for now and have real time updates, the user needs to be able to see the lines being drawn to the canvas.
Even if setTimeout did work on such extremely small time frames this would probably not have worked out. When you use callbacks and/or promises you rely of JS runtime's event loop. This event loop only executes your callback as fast as it can. The architecture is going to impose lags that will become visible when you go below 1ms. The callback in setTimeout is not exactly executed after N ms passes. After N ms passes it only becomes eligible to be executed. And it gets invoked finally only when its turn comes on another event loop tick.
As for your second approach it does not exactly "use up resources". The thing is you no longer use event loop. But you must remember that JS is single-threaded. And because of it when JS-code executes non-stop it will not let user interact with UI at all. User can do something only between event callback executions. So don't ever use long running whiles in JS in browser unless you want to ruin user experience. Maybe unless you use Web workers because they will let you create new threads, but then you wouldn't be able to draw anything from there.
In general your approach to animation as "drawing something and then sleeping" is rather naive. Performant and smooth animations are what the video cards are made for although writing it in browser to efficiently utilize video card may be tricky. If you want to make animation in browser then you have to find specific browser function calls made specifically for animation on a Canvas or WebGL.
Maybe start here:
https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Basic_animations
Also think about if you actually need so may frames per second. Over 1000fps? Can the monitor make it? What about performance impact?

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.

js setTimeout synchronize

In fact, when I use
setTimeout(a(),60);
setTimeout(a(),120);
setTimeout(a(),180);
setTimeout(a(),240);
It is supposed to be 60ms gap between calling's of a functions.
But it isnt, especially when it is fired during page loading or animating elements. In fact that gap gets even 2x longer when browser 'has hard work to do'. In some cases it can be visible easly.
The point of question is - is there any other way to synchronize events or functions in time in javascript?
The timing in setTimeout(a(),60) in simple terms translates to I will run this function no earlier than 60ms, but if I get busy it could be later than that.
Therefore, setTimeout does not promise when the execution will take place, only that it will take place sometime after the given time in milliseconds.
So to answer your question, no there is no way to guarantee execution time with setTimeout but you can load your script after the DOM has loaded so that JavaScript is not busy anymore loading other things. In jQuery you can use the $(document).ready() function for that purpose.
Read this article by John Resig for more information about timing in JavaScript: http://ejohn.org/blog/how-javascript-timers-work/
Try this:
setTimeout(a,60);
setTimeout(a,120);
setTimeout(a,180);
setTimeout(a,240);
Note that the function doesn't have the ()s.
In your particular case, setInterval() might work:
var count = 0, interval = setInterval(function() {
count += 1;
if (count > 4) {
clearInterval(interval);
} else {
a();
}
}, 60);
Note that jQuery has a built-in animation feature that uses the different, better approach of simply treating an animation as a function of time and frequently checking the clock, so an unexpected delay would simply make the animation a bit less smooth.

Javascript setinterval() lag in Firefox?

I have wrote this code to make seconds (with decisec & centisec) counting up.
You've wasted time <span id="alltime">0.00</span> seconds.
<script type="text/javascript">
function zeroPad(num,count)
{
var numZeropad = num + '';
while(numZeropad.length < count) { numZeropad = "0" + numZeropad; }
return numZeropad; }
function counttwo() {
tall = document.getElementById('alltime').innerHTML;
if(parseFloat(tall) < 1.00) { tnew2 = tall.replace('0.0','').replace('0.',''); }
else { tnew2 = tall.replace('.',''); }
tnum = parseInt(tnew2) + 1;
//check if have to add zero
if(tnum >= 100) { tstr1 = tnum + ''; }
else { tstr1 = zeroPad(tnum,3); }
tlast = tstr1.substr(0,tstr1.length - 2) + '.' + tstr1.substr(tstr1.length - 2);
document.getElementById("alltime").innerHTML = tlast;
}
var inttwo=setInterval("counttwo()",10);
</script>
In HTML document and run.
It works well but when I use Firefox 4 and run the code. Seems like it LAG a bit (stop a bit before counting up) when it's on some numbers (randomly like 12.20, 4.43). I've tried change "counttwo()" to counttwo but that doesn't help.
I have told some of my friends to run on Firefox 4 too. They said it doesn't lag at all. This cause because of my computer ? or My Firefox ? or something else ?
Thanks in advance!
PS. Fiddle here: http://jsfiddle.net/XvkGy/5/ Mirror: http://bit.ly/hjVtXS
When you use setInterval or setTimeout the time interval is not exact for several reasons. It is dependent on other javascript running, the browser, the processor etc. You have to take a reliability of +- 15ms for granted afaik. See also ...
That's a lot of counting, so on some computer, yes, it might lag (if it's a prehistoric one or the user's got his processor really busy with something), also if I'm right, that thing won't work with Chrome's V8, since that script would freeze if you switched tabs, and resume executing only when you return to that tab.
If you're just seeing pauses every so often, you're probably seeing garbage collection or cycle collection pauses.
You can test this by toggling your javascript.options.mem.log preference to true in about:config and then watching the error console's "Messages" tab as your script runs. If the GC/CC messages are correlated with your pauses, then they're the explanation for what you see.
As for why you see it but others don't... do you see the problem if you disable all your extensions?
The problem with setInterval is that it can eventually lead to a back-up. This happens because the JavaScript engine tries to execute the function on the interval (in your case, 10ms), but if ever that execution takes longer than 10ms, the JS engine starts trying to execute the next interval before the current one stops (which really just means it queues it up to run as soon as the previous callback finishes).
Since JavaScript executes single-threaded (with the exception of web workers in HTML 5), this can lead to pauses in your UI or DOM updates because it is continuously processing JavaScript callbacks from your setInterval. In worst case scenarios, the whole page can become permanently unresponsive because your stack of uncompleted setInterval executions gets longer and longer, never fully finishing.
With a few exceptions, it is generally considered a safer bet to use setTimeout (and invoking the setTimeout again after execution of the callback) instead of setInterval. With setTimeout, you can ensure that one and only one timeout is ever queued up. And since the timers are only approximate anyway (just because you specify 10ms doesn't mean it will happen at exactly 10ms), you don't typically gain anything from using setInterval over setTimeout.
An example using setTimeout:
var count = function(){
// do something
// queue up execution once again
setTimeout(count, 10);
};
count();
One reason why you may see pauses on some browsers, and not others, is because not all JavaScript engines are created equal :). Some are faster than others, and as such, less likely to end up with a setInterval backup.
Different browsers use different JavaScript engines, so it's possible that this code just finds a spot where Firefox's JägerMonkey scripting engine has some problems. There doesn't seem to be any obvious inefficiencies in the counting itself..
If it's working on your friends' installs of FF4, then it's probably just an isolated problem for you, and there isn't much you'll be able to do by changing the code.

javascript race condition

I'm calling a javascript function that sets the opacity of an iframe an unknown amount of times in rapid succession. Basically this tweens the alpha from 0 to 100.
here is the code
function setAlpha(value)
{
iframe.style.opacity = value * .01;
iframe.style.filter = 'alpha(opacity =' + val + ')';
}
My problem is that for the first time it is working in ie (7) and not in firefox (3.02). in Firefox I get a delay and then the contentdocument appears with an opacity of 100. If I stick an alert in it works, so I'm guessing it is a race condition (although I thought javascript was single threaded) and that the setAlpha function is being called before the last function has finished executing.
Any help would be greatly appreciated. I've read the 'avoiding a javascript race condition post' but I think this qualifies as something different (plus I can't figure out how to apply that example to this one).
The issue is that most browsers don't repaint until there is a pause in the javascript execution.
This can be solved by using setTimeout, as others have suggested. However, I recommend using something like jQuery, or any of the javascript libraries to do animations. Running setTimeout 100 times is a bad idea because the length of the animation will vary based on the browser and speed of the user's computer. The correct way to do animations, is to specify how long they should last and check the system time to determine how far the animation should progress.
function fadeIn(elem,animation_length) {
var start = (new Date()).getTime();
var step = function() {
window.setTimeout(function() {
var pct = ((new Date()).getTime() - start)/animation_length;
elem.style.opacity = Math.min(pct,1);
if (pct < 1)
step();
},20);
};
step();
}
[edit:] The code above is only to illustrate how to do animations based on the system clock instead of simple intervals. Please use a library to do animations. The code above will not work on IE, because IE uses "filter:opacity(xx)" instead of "opacity". Libraries will take care of this for you and also provide nice features such as completion events, and the ability to cancel the animation.
Javascript doesn't run across multiple threads so you're safe from race conditions (ignoring upcoming Worker thread support in Safari and Firefox :D ).
Simple question, how are you calling setAlpha multiple times, firefox, safari and opera all coalesce style sheet updates -- eg. they won't repaint or even recalc style info while js is running unless they have to. So they will only paint if JS has completed.
So if you're doing
while(...) setAlpha(...)
they won't update, you'll probably need to use setTimeout to trigger multiple distinct calls to update the style.
An alternative would be to use a library such as jQuery, mootools,etc that i vaguely recall provide a simplified mechanism to do these types of animations and transitions. As an added bonus i believe at least a few libraries will also use webkit transition and animation css rules when available (eg. Safari, and i think the latest firefox builds)
[edit: caveat: i haen't actually used any of these libraries, i only read about what they're supposed to do. My sites render the same in lynx as any other browser because i couldn't design my way out of a paper bag :D ]
Are you using setTimeout or a tight loop? If you're using just a loop to call the function, then switch to using setTimout.
example:
function setAlpha(value)
{
iframe.style.opacity = value * .01;
iframe.style.filter = 'alpha(opacity =' + val + ')';
if(value < 100 ) {
setTimeout(function () {setAlpha(value+1)},20);
}
}
setAlpha(0);
Because you see, it's not just javascript that's single threaded. It's the whole damn browser. If your javascript goes into a tightloop, you hang the whole browser. So the browser pauses waiting for javascript to finish, and doesn't even have a chance to update the screen, while your code is rapidly changing some dom values.
Some browsers are smart enough to delay changes to the DOM until the call stack is empty.
This is a generally a smart thing to do. For example, if you call a function that changes an element to yellow, and immediately call a function that changes the same element back to it's original state, the browser shouldn't waste time making the change, since it should happen so quickly as to be imperceptible to a user.
The setTimeout(func, 0) trick is commonly used to force Javascript to delay execution of func until the call stack is empty.
In code:
function setAlpha(opacity){
some_element.style.opacity = opacity;
}
/**
* This WON'T work, because the browsers won't bother reflecting the
* changes to the element's opacity until the call stack is empty,
* which can't happen until fadeOut() returns (at the earliest)
**/
function fadeOut(){
for (var i=0; i<10; i++){
setAlpha(0.1*i);
}
}
/**
* This works, because the call stack will be empty between calls
* to setAlpha()
**/
function fadeOut2(){
var opacity = 1;
setTimeout(function setAlphaStep(){
setAlpha(opacity);
if (opacity > 0){
setTimeout(setAlphaStep, 10);
}
opacity -= 0.1;
}, 0);
}
All this boils down to being a wonderful excuse to use one of many javascript libraries that handle this tricky stuff for you.
Edit: and here's a good article on the tricky Javascript call stack

Categories