You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

嵌套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 

同时存在两个核心疑问:

  1. 浏览器即将重绘时,外层rAF回调执行do_1st_thing后再次调用rAF,此时浏览器会继续重绘,将后续rAF排入下一次重绘队列,还是立即执行后续回调?
  2. 若嵌套回调执行间存在重绘,是否能保证它们之间必然会有重绘?

*注:若立即执行所有回调,会出现如下顺序:

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 07:35:07