我有一个小项目,我正在努力消耗twitter流API并从中制作一个小画布动画。鉴于twitter流API不会结束,动画可以无限期地继续。
这就是问题所在。 requestAnimationFrame
似乎通过递归操作,我们在ES6之前没有得到正确的尾调用,这意味着我认为这会增加每帧的调用堆栈。
问题是,我是对的,这最终会因为超出最大调用堆栈大小而引发错误,或者浏览器是否会发挥作用以避免限制? requestAnimationFrame
真的做了一些我不理解的奇怪事情(或许沿着setInterval
没有递归的方式)?
在chrome 36.0.1985.32 beta(调用堆栈大小为20834)中,我正在测试:
function test(t) {
requestAnimationFrame(test);
}
test(performance.now());
并且没有看到任何问题。假设60fps,我会期待一个RangeError
被抛出~6分钟。
另一个误导性信息显示在chrome开发人员工具窗口的“调用堆栈”部分中,其中显示了requestAnimationFrame
调用堆栈,因为它将填满堆栈,如下图所示:
RAF将在“下一个绘制的框架”上启动功能。这意味着它将在另一个操作堆栈中执行,并且您将不会有任何最大的调用堆栈错误。
如果requestAnimationFrame
调用test
,那么确实会有一个无限调用堆栈。很明显,test
确实称requestAnimationFrame
。需要验证requestAnimationFrame
是否称为test
。
以下代码将找出:
function testSnitch(t) {
var caller = arguments.callee.caller || 'NULL';
console.log(caller.toString());
requestAnimationFrame(test);
}
testSnitch(performance.now());
是的,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()完成后再次运行并解除其异步堆栈。根据您的代码,它可能会运行一次,或者它可能会启动一个全新的动画循环。