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

requestAnimationFrame何时触发?为何需等JS代码执行完才生效?

为什么requestAnimationFrame没有按预期执行?

嘿,这个问题其实戳中了JavaScript单线程模型的核心点——这也是很多刚接触requestAnimationFrame的开发者容易踩的坑!

首先得明确一个关键事实:JavaScript是单线程执行的,而requestAnimationFrame的回调函数并不是立即执行的——它会被浏览器加入到「下一次重绘前的回调队列」里,但只有当主线程完全空闲时,这个队列里的任务才会被执行。

咱们来拆解你这段代码的执行流程,就能明白为什么看不到倒计时效果:

  1. 初始化变量n=0,定义sleep和refresh函数
  2. 重置n=0,调用window.requestAnimationFrame(refresh)——这一步只是把refresh函数“预约”到浏览器的下一次重绘时机,但此时它并没有执行
  3. 执行n = n+1,n变成1;接着调用sleep(2000)——这个sleep是用同步循环实现的,会死死占用主线程2秒钟,期间浏览器根本无法处理任何回调(包括requestAnimationFrame的回调),页面也不会有任何更新
  4. 2秒后,n变成2,再次调用sleep(2000)——主线程又被阻塞2秒,同样无法处理回调
  5. 又过2秒,n变成3,整个脚本终于执行完毕,主线程空闲了!这时候浏览器才会执行之前预约的refresh函数,把body的文本设置为3——所以你最终只能看到这个结果

怎么实现预期的倒计时效果?

要避免同步阻塞主线程,得用异步的方式来实现延迟。这里给你两种可行的方案:

方案1:用requestAnimationFrame递归实现倒计时

let n = 3;

function refresh() {
  const body = document.body;
  body.textContent = n;
  n--;
  if (n >= 0) {
    // 用setTimeout控制1秒间隔,requestAnimationFrame保证重绘时机
    setTimeout(() => window.requestAnimationFrame(refresh), 1000);
  }
}

// 启动倒计时
window.requestAnimationFrame(refresh);

方案2:用async/await模拟异步延迟

async function sleep(ms) {
  // 用Promise实现真正的异步延迟,不会阻塞主线程
  await new Promise(resolve => setTimeout(resolve, ms));
}

async function startCountdown() {
  let n = 0;
  const body = document.body;

  function refresh() {
    body.textContent = n;
  }

  while (n <= 3) {
    window.requestAnimationFrame(refresh);
    n++;
    await sleep(2000);
  }
}

startCountdown();

这两种方式都不会阻塞主线程,requestAnimationFrame的回调能在每次延迟后及时执行,从而实现你想要的倒计时效果。

内容的提问来源于stack exchange,提问作者Phillipp Schwarz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 09:29:33