嵌套requestAnimationFrame调用:重绘与回调执行顺序疑问
requestAnimationFrame 执行机制解析
核心疑问与示例代码
我正试图理解requestAnimationFrame(以下简称rAF)的工作机制,明确使用它时的预期行为。已知调用rAF是告知浏览器,希望在下次重绘前执行传入的回调函数,且该函数必然会在下次重绘前被调用,但重绘不一定会在函数执行后立即发生。
示例嵌套调用代码:
requestAnimationFrame(() => { do_1st_thing(); requestAnimationFrame(() => { do_2nd_thing(); requestAnimationFrame(() => { do_3rd_thing(); requestAnimationFrame(() => { do_4th_thing(); requestAnimationFrame(() => { do_5th_thing(); }) }) }) }) });
我曾推测两种可能的执行顺序,但不确定背后的逻辑:
推测顺序1:
1st thing repaint 2nd thing repaint 3rd thing repaint 4th thing repaint 5th thing repaint
推测顺序2:
1st thing 2nd thing repaint 3rd thing 4th thing repaint 5th thing repaint
同时存在两个核心疑问:
- 浏览器即将重绘时,外层rAF回调执行
do_1st_thing后再次调用rAF,此时浏览器会继续重绘,将后续rAF排入下一次重绘队列,还是立即执行后续回调? - 若嵌套回调执行间存在重绘,是否能保证它们之间必然会有重绘?
*注:若立即执行所有回调,会出现如下顺序:
thing 1 thing 2 thing 3 thing 4 thing 5 repaint
机制解析与答案
1. 嵌套rAF的执行逻辑
浏览器的渲染是按离散的渲染周期运行的(通常60fps,约16.6ms/周期),每个周期的核心流程为:
事件处理 → 执行当前周期的rAF回调队列 → 布局计算 → 页面绘制(重绘)
当在一个rAF回调内部再次调用rAF时,新注册的回调会被加入下一个渲染周期的rAF队列,而非当前周期。因为当前周期的rAF回调队列已经处于执行阶段,新添加的回调无法插队到本次周期执行。
2. 实际执行顺序
针对上述示例代码,实际执行顺序必然是:
do_1st_thing(); [完成当前渲染周期,触发重绘] do_2nd_thing(); [完成当前渲染周期,触发重绘] do_3rd_thing(); [完成当前渲染周期,触发重绘] do_4th_thing(); [完成当前渲染周期,触发重绘] do_5th_thing(); [完成当前渲染周期,触发重绘]
核心疑问解答
- 疑问1:浏览器不会立即执行后续嵌套的rAF回调,而是将它们排入下一次重绘前的队列。当前回调执行完成后,浏览器会继续完成当前渲染周期的布局、绘制步骤,下一个渲染周期到来时,才会执行新注册的rAF回调。
- 疑问2:正常前台页面下,嵌套的rAF回调之间必然会有重绘。因为每个嵌套的rAF都注册到下一个渲染周期,而每个渲染周期结束时都会触发页面绘制(除非页面处于后台、最小化等不需要视觉更新的状态,此时浏览器会暂停渲染周期)。
你之前推测的“连续执行多个回调再重绘”的情况不会发生,rAF的注册规则从根本上避免了这种场景。
内容的提问来源于stack exchange,提问作者nova
相关产品推荐
相关产品推荐

