Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Endless animations, requestAnimationFrame and call stack limits

I have a little project that I'm working on that consumes the twitter streaming API and makes a little canvas animation from it. Given that the twitter streaming API doesn't conclude, the animation could go on indefinitely.

Therein lies the problem. requestAnimationFrame appears to operate through recursion, and we don't get proper tail calls until ES6, meaning that I think this grows the call stack for every frame.

The question is, am I right that this'll eventually raise an error for exceeding the maximum call stack size, or does the browser play a trick to avoid the limit? Is requestAnimationFrame really doing something strange that I don't understand (perhaps along the lines of a setInterval not being recursive)?

In chrome 36.0.1985.32 beta (which has a call stack size of 20834), I am testing with:

function test(t) {
    requestAnimationFrame(test);
}

test(performance.now());

And have seen no issues. I would expect an RangeError to be thrown ~6 minutes assuming 60fps.

Another misleading information is shown in the Call Stack section of the chrome developer tools window, where it is shown the requestAnimationFrame Call stack as it would fill up the stack, as show in the following image:

enter image description here

like image 684
qubyte Avatar asked Jun 01 '14 11:06

qubyte


People also ask

What makes requestanimationframe so awesome?

What makes requestAnimationFrame so awesome is that it doesn't force the browser to do a repaint that may never happen. Instead, it asks the browser nicely to call your animation loop when the browser decides it is time to redraw the screen. This results in no wasted work by your code on screen updates that never happen.

How many frames per second does a requestanimationframe call?

This value simulates how often the real requestAnimationFrame () will typically be called by the browser each time it's invoked based on the typical user's screen refresh rate of 60 frames per second. The syntax for requestAnimationFrame is very straightforward: ?

How do I cancel a requestanimationframe call?

Just like with setTimeout / setInterval, you can cancel a requestAnimationFrame call, and in identical fashion as well. requestAnimationFrame when called returns a non 0 integer that can be captured inside a variable and passed into its nemesis counterpart cancelAnimationFrame () to stop it from being invoked again.

How many frames per second (FPS) does an HTML animation refresh?

That same holds for us in the HTML world also. With requestAnimationFrame, your frame rate is typically around 60 frames per second (FPS). To repeat that differently, this means your requestAnimationFrame function and related code have the potential to refresh your screen 60 times every second.


2 Answers

RAF will launch the function "on the next drawn frame". That's means that it will be executed in another stack of action, and you won't have any maximum call stack error.

like image 192
ArnaudMolo Avatar answered Sep 25 '22 09:09

ArnaudMolo


Yes, requestAnimationFrame() is asynchronously recursive, and that prevents any actual stack overflow. But don't forget that the stack still unwinds at the end. There is no issue if you're running a single animation. But if you are running a series of animations in sequence, you might do something like this:

function animateFirst(timeStamp) {
    let r = functionReturnValue();
    if (r == "complete") {
        frame = requestAnimationFrame(animateNext);
        return; // this is necessary
    }
    frame = requestAnimationFrame(animateFirst);
}

Or you must structure it this way:

function animateFirst(timeStamp) {
    let r = functionReturnValue();
    if (r == "complete") {
        frame = requestAnimationFrame(animateNext);
    }
    else {
        frame = requestAnimationFrame(animateFirst);
    }
}

These examples are oversimplifications. In an actual animation function there might be more complex logic. The point is that if you leave out the return statement in the first example, animateFirst() will run again after animateNext() has completed and unwound its async stack. Depending on your code, it might run once, or it might start a whole new animation loop.

like image 41
jamess Avatar answered Sep 22 '22 09:09

jamess