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:
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.
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: ?
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With