requestAnimationFrame驱动的JavaScript代码为何未触发无限递归超时?请解析程序执行流程
为什么这段requestAnimationFrame代码不会触发无限递归超时?
先来看这段代码:
function main(ctime){ window.requestAnimationFrame(main); if(start==undefined){ start= ctime; } if((ctime-start)/1000<0.5){ console.log(ctime); return; } } window.requestAnimationFrame(main);
很多人第一眼看到会觉得“这不是无限递归吗?每次调用main都会再调用requestAnimationFrame(main),岂不是会一直跑下去直到超时?”但实际运行起来完全不会有这个问题,原因得从requestAnimationFrame的工作机制和代码逻辑两方面说:
1. 代码的完整执行流程
- 初始触发:第一次调用
window.requestAnimationFrame(main),把main函数加入浏览器的帧回调队列,等待下一次浏览器重绘时执行。 - 前0.5秒内的循环:
- 浏览器准备重绘时,调用
main并传入当前时间戳ctime。 - 刚进入
main就执行window.requestAnimationFrame(main)——这一步只是把下一次的main回调加入队列,不是立即执行,不会像同步递归那样直接堆叠调用栈。 - 第一次执行时
start未定义,所以把start赋值为当前ctime。 - 判断时间差小于0.5秒,打印
ctime后return,函数结束。 - 浏览器每16~17ms(对应60fps的帧间隔)会重复这个流程,直到时间差超过0.5秒。
- 浏览器准备重绘时,调用
- 0.5秒后的执行:
- 进入
main后依然先调用requestAnimationFrame(main),把下一次回调加入队列。 - 此时
start已经定义,跳过赋值步骤。 - 判断时间差≥0.5秒,不会执行
console.log和return,函数正常走完所有代码后结束。 - 后续浏览器仍会按帧间隔执行队列里的
main,但每次都是执行轻量的判断逻辑后结束。
- 进入
2. 不会超时/栈溢出的核心原因
这根本不是真正的同步递归,而是浏览器帧循环驱动的异步回调:
- 异步执行机制:
requestAnimationFrame不会在当前调用栈里直接嵌套调用函数,而是把回调放到独立的异步队列中。每次回调执行都是在全新的调用栈里,执行完就销毁,不会造成调用栈堆叠溢出。 - 浏览器的自动调控:浏览器只会在准备重绘、有空闲资源的时候触发
requestAnimationFrame回调,即使一直往队列里加回调,也只是每帧执行一次轻量的判断逻辑,完全达不到超时的资源占用阈值。
简单说,它和同步递归的执行逻辑完全不同,自然不会出现超时问题。
内容的提问来源于stack exchange,提问作者Ashwin Baranwal
相关产品推荐
相关产品推荐

