Question

I'm using the requestAnimFrame method in JavaScript to make my canvas update within my program's main loop:

window.requestAnimFrame = (function(callback) {
  return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame ||
    function(callback) {
      window.setTimeout(callback, 1000 / 60);
    };
})();


function animate() {
    requestAnimationFrame( animate );
//var runcount = 100;
//for (var i=0;i<=100;i++) {
    draw();
//    if (runcount === i)
//        alert("Completed program loop");
//    }
}

What was happening was that my program that only updated the canvas after my main loop had run for 100 iterations and then stopped. After I add the method above into my main loop, suddenly I get canvas updates on every loop of my program.

The fact that the canvas didn't get updated until my program had finished made me think that the canvas update was running in a different thread that didn't get priority. (But we know that JavaScript is single-threaded).

My question is - Can we consider an requestAnimFrame a 'yield' on the application loop to allow the event loop to process? Can I still assume that JavaScript is single threaded?

Was it helpful?

Solution

You are not specific about how you did your iteration 1000 times, but I'll assume you mean you iterated using a for or while loop (? correct me if I'm wrong..).

When you do a for/while loop the script will be busy-looping until that loop (or scope) has completed. As JS is as you know single-threaded it cannot process things such as the event queue while it is doing this - therefor nothing will be able to update (incl. DOM) until the loop has finished (generally speaking, there may be browser specific implementation that do allow DOM updates if DOM updates are run on a separate thread for example but this is a separate thing from JS).

Now that you implemented rAF you are doing a single iterating per frame. This means the browser has time to process the event queue asynchronously (not the same as multi-threaded) unless the code you processed in the loop also did busy-looping for a while which would create the same scenario as the first.

You could have used setTimeout as well. What rAF do differently than for example setTimeout or setInterval is that it is an efficient low-level implementation of a timer mechanism able to synchronize to the monitor's VBLANK period (vertical blanking which the video card slaves to through an option called vsync) - meaning in plain word the monitor's refresh rate (typically 60 Hz). This allow us to create smooth(er) animation and that is why it's called request*Animation*Frame as this is what it's primarily made for.

Is JS still single-threaded? Yes, this won't change (the only way to achieve multi-threading in JS is by using Web Workers).

Do rAF change anything? Not in this area - it's a more accurate and performant alternative to setTimeout and it syncs differently but that's all. It will suffer the same restrictions as with setTimeout not being able to trigger if a scope is busy (hence the request* part of the name which implies no guarantee) but when it do trigger it will be in sync with the monitor refresh rate.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top