15

(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

Pacerier
  • 86,231
  • 106
  • 366
  • 634
rafaelcastrocouto
  • 11,781
  • 3
  • 38
  • 63
  • I'm not really sure if this does is doing the same thing, but check out this hack using `postMessage`: http://codepen.io/anon/pen/wczFv – Dogbert Nov 11 '13 at 13:22
  • `setTimeout` is limited to 4ms in most browsers, explaining why you got around 250 for that. – Qantas 94 Heavy Nov 11 '13 at 13:23
  • @Dogbert, I tested here https://www.quora.com/Does-JavaScript-in-the-browser-have-the-equivalent-of-process-nextTick-or-setImmediate-in-node-js-or-do-we-just-have-setTimeout/comment/6233464 seems like mutation observer wins Promise. – Pacerier Aug 12 '17 at 08:11
  • 1
    I doubt animation-frame and post-message can win mutation observer, but untested. – Pacerier Aug 12 '17 at 08:12

3 Answers3

32

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.

vsync
  • 118,978
  • 58
  • 307
  • 400
Spudley
  • 166,037
  • 39
  • 233
  • 307
  • @rafaelcastrocouto: No problem. Glad to help. (Um, give me points? you can do, and you have done.... you upvoted the answer and ticked it to accept, unless you mean something else?) – Spudley Nov 11 '13 at 16:29
  • you deserved some extra rep! gratz – rafaelcastrocouto Nov 11 '13 at 18:14
  • I noticed that [in some cases](https://travis-ci.org/parametric-svg/html/builds/80921178) `requestAnimationFrame` fires before the mentioned [YuzuJS/setImmediate](https://github.com/YuzuJS/setImmediate) shim. I think it might happen when the browser is busy with calculation – in that case it may favor smooth animation over strict `postMessage` spec compliance. But that’s just an assumption. BTW, the results come from *electron* which is built on *Chromium*. – tomekwi Sep 17 '15 at 22:17
  • 2
    "This feature is non-standard and is not on a standards track. Do not use it on production sites facing the Web: it will not work for every user. There may also be large incompatibilities between implementations and the behavior may change in the future." – Andrew Apr 16 '17 at 17:32
7

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();
Gershom Maes
  • 7,358
  • 2
  • 35
  • 55
Esailija
  • 138,174
  • 23
  • 272
  • 326
1

Use postMessage() as described in this blog.

Krizz
  • 11,362
  • 1
  • 30
  • 43