是requestAnimationFrame实现's递归

Is requestAnimationFrame implementation's recursive?

本文关键字:递归 requestAnimationFrame 实现      更新时间:2023-09-26

我目前正在试验three.js,它依赖于requestAnimationFrame来执行动画。

在调用立方体旋转和renderer.render函数之前,以下代码不会导致无限递归吗?

function render() {
    requestAnimationFrame(render);
    cube.rotation.x += 0.1;
    cube.rotation.y += 0.1;
    renderer.render(scene, camera); 
}
render();

代码是有效的,但我正在努力提高我对JavaScript的整体理解。

在我看来,render是作为回调函数调用的。但这是否意味着JavaScript在停止执行下一个调用之前继续运行函数中的代码?

这只要求浏览器在下一个渲染循环之前调用回调:

只要准备好在屏幕上更新动画,就应该调用此方法。这将要求在浏览器执行下一次重新绘制之前调用动画函数。

所以这里没有递归,函数继续执行。

您也可以使用cancelAnimationFrame取消回调请求。

看这里。

不,堆栈不会爆炸,因为requestAnimationFrame的回调是异步执行的。在清除整个调用堆栈之前,异步代码永远不会运行。有关事件循环的说明,请参阅此视频。

考虑这个演示来说明:

const fakeRAF = cb => setTimeout(cb, 1000 / 60);
(function rerender() {
  fakeRAF(rerender);
  console.log(performance.now());
})();

你可以让它运行一整天。当然,requestAnimationFramefakeRAF做的工作多得多,因为它在计算何时触发cb方面非常聪明,但这个简单的演示足以证明它不是递归的,这要归功于setTimeout提供了异步API。

顺便说一下,在函数的顶部还是底部调用requestAnimationFramefakeRAF并不重要。函数中的所有同步代码都必须在触发回调之前运行,这样就不会出现下一帧踩在上一帧上或类似的问题。

在第一次render()调用之后,RequestAnimationFrame将异步处理渲染函数,每秒调用它约60次。您的函数不是递归的,因为Javascript将继续执行代码。

但是,您应该只在渲染函数的最后使用RequestAnimationFrame,作为函数的最后一个命令。想象一下,你有一个大场景,有很多动画:在完成参数更新之前请求下一帧,可能会造成巨大的混乱。

您也可以使用setTimeout来处理动画,调用具有所需帧速率的渲染方法,如下所示:

var desideredFrameRate = 60;
window.myRequestAnimationFrame = function(callback) {
    setTimeout(callback, 1000/desiredFrameRate);
}