无尽的动画,requestAnimationFrame和调用栈限制

13
我正在做一个小项目,使用Twitter流API并从中制作出一个小型画布动画。由于Twitter流API没有结束,因此动画可能会无限进行。
这就是问题所在。requestAnimationFrame似乎通过递归来运行,而且在ES6之前我们没有得到恰当的尾调用,这意味着我认为这会为每一帧增加调用堆栈。
问题是,我是否正确,这最终会引发超过最大调用堆栈大小的错误,还是浏览器会玩一个技巧以避免限制?requestAnimationFrame是否真的在做我不理解的奇怪事情(也许是类似于setInterval不是递归的东西)?
在Chrome 36.0.1985.32 beta中(其调用堆栈大小为20834),我正在进行测试:
function test(t) {
    requestAnimationFrame(test);
}

test(performance.now());

我并没有发现任何问题。假设每秒60帧,我预计会在大约6分钟后抛出一个RangeError

Chrome开发者工具窗口中的调用堆栈部分显示了另一个误导性信息,它显示了requestAnimationFrame调用堆栈,似乎会填满整个堆栈,如下图所示:

enter image description here


1
你是否从阅读文档开始,它可以很好地解释这个问题。 - adeneo
7
文档没有清楚地说明上述问题的答案。 - qubyte
就我所见,最接近的解释是“这将请求在浏览器执行下一次重绘之前调用您的动画函数”,这意味着它不是递归的。但我希望得到更明确的解释。 - qubyte
是的,我太傻了。它不是同步递归。 - qubyte
不确定您所说的“非递归”是什么意思,该函数本身当然是非递归的,但将其放置在被递归调用的函数内使其成为递归函数,并且它每秒调用回调函数约60次,具体取决于浏览器,所有这些都在文档中有说明。由于它是异步的并且在下一次重绘之前调用回调函数,因此不会出现调用堆栈错误。 - adeneo
显示剩余2条评论
3个回答

6

RAF将启动“在下一帧绘制时”执行的功能。这意味着它将在另一个动作堆栈中执行,您将不会遇到任何最大调用堆栈错误。


啊,真是个脑抽。是的,任何类型的异步函数都没有限制,因为每个函数都在不同的事件循环迭代中... - qubyte

2

是的,requestAnimationFrame() 是异步递归的,这可以防止实际的堆栈溢出。但不要忘记在最后堆栈仍然会展开。如果您只运行单个动画,则没有问题。但如果您按顺序运行一系列动画,则可能会像这样操作:

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

或者你必须按照以下方式构建它:
function animateFirst(timeStamp) {
    let r = functionReturnValue();
    if (r == "complete") {
        frame = requestAnimationFrame(animateNext);
    }
    else {
        frame = requestAnimationFrame(animateFirst);
    }
}

这些例子都是过于简单的。在实际的动画函数中可能会有更复杂的逻辑。重点是,如果在第一个例子中省略了return语句,那么animateFirst()将在animateNext()完成并解开其异步堆栈后再次运行。根据您的代码,它可能只运行一次,或者可能启动一个全新的动画循环。


0
如果requestAnimationFrame调用test,那么将会有一个无限的调用堆栈。很明显test确实调用了requestAnimationFrame。需要验证requestAnimationFrame是否调用了test
以下代码将找出答案:
function testSnitch(t) {
    var caller = arguments.callee.caller || 'NULL';
    console.log(caller.toString());
    requestAnimationFrame(test);
}

testSnitch(performance.now());

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接