requestAnimationFrame和requestIdleCallback分别是什么,是用在什么场景下

深入解析 requestAnimationFrame 和 requestIdleCallback


requestAnimationFrame (rAF) 和 requestIdleCallback (rIC) 都是浏览器提供的 异步调度 API,但它们的执行时机和用途完全不同。

API 主要用途 何时执行 是否保证执行 适合场景
requestAnimationFrame 高优先级 UI 更新 下一帧渲染前(通常 16.67ms 内) ✅ 一定执行(下一帧到来时) 动画、滚动、WebSocket 实时数据可视化
requestIdleCallback 低优先级任务 浏览器空闲时(时间不确定) ❌ 不一定执行(如果浏览器一直忙) 预加载、日志统计、AI 计算、非关键数据处理

1. requestAnimationFrame (rAF):下一帧渲染前执行

🔹 工作原理

浏览器的 渲染帧率 一般是 60Hz ,即 每 16.67ms 需要完成一次绘制(包括 JS 计算、样式计算、布局、绘制等)。

  • requestAnimationFrame 会在下一帧开始前执行 ,保证代码在下一帧渲染前完成,不会阻塞 UI。

  • 适用于 高频 UI 更新,比如:

    • CSS 动画
    • 页面滚动
    • WebSocket 高频推送的数据可视化
    • Canvas 绘图

🔹 示例 1:流畅动画

ini 复制代码
let box = document.getElementById("box");
let position = 0;

function move() {
  position += 5;
  box.style.transform = `translateX(${position}px)`;

  if (position < 300) {
    requestAnimationFrame(move); // 递归调用,持续更新
  }
}

requestAnimationFrame(move);

✅ 为什么使用 requestAnimationFrame?

  • 不卡顿:会在合适的时间点执行,不会阻塞主线程。
  • 不影响页面性能:浏览器空闲时自动暂停,节省资源。

🔹 示例 2:WebSocket 高频推送数据

ini 复制代码
let pending = false;

socket.onmessage = (event) => {
  const data = JSON.parse(event.data);

  if (!pending) {
    pending = true;
    requestAnimationFrame(() => {
      updateUI(data);
      pending = false;
    });
  }
};

function updateUI(data) {
  document.getElementById("output").innerText = JSON.stringify(data);
}

📌 避免 UI 频繁更新,导致卡顿

  • 如果 WebSocket 每 1ms 推送一次数据,直接更新 DOM 会让页面卡死。
  • 使用 requestAnimationFrame 让 UI 更新保持在 60FPS 以内,保证流畅度。

🔹 示例 3:滚动优化

监听 scroll 事件时,直接操作 DOM 可能导致页面卡顿,应该用 requestAnimationFrame 优化:

ini 复制代码
let ticking = false;

window.addEventListener("scroll", () => {
  if (!ticking) {
    requestAnimationFrame(() => {
      updateScrollPosition(window.scrollY);
      ticking = false;
    });
    ticking = true;
  }
});

function updateScrollPosition(scrollY) {
  document.getElementById("position").innerText = scrollY;
}

📌 好处 :避免 scroll 事件触发过于频繁,优化滚动时的 UI 更新。


2. requestIdleCallback (rIC):浏览器空闲时执行

🔹 工作原理

  • requestIdleCallback 让浏览器在 主线程空闲时 执行低优先级任务。

  • 执行时间不固定,取决于浏览器的空闲情况:

    • 如果用户在快速滚动 ,可能不会触发 requestIdleCallback。
    • 如果 CPU 负载高,可能很久都不会执行。

🔹 示例 1:处理后台任务

scss 复制代码
function heavyTask(deadline) {
  while (deadline.timeRemaining() > 0) {
    console.log("执行低优先级任务...");
  }

  requestIdleCallback(heavyTask);
}

requestIdleCallback(heavyTask);

📌 deadline.timeRemaining() 返回本次空闲时间的剩余毫秒数,避免影响 UI 交互。


🔹 示例 2:懒加载数据

当页面加载完毕后,在空闲时间预加载一些不重要的数据:

javascript 复制代码
requestIdleCallback(() => {
  fetch("/api/data").then(res => res.json()).then(data => {
    console.log("预加载数据完成:", data);
  });
});

📌 好处:不影响页面主线程,提高用户体验。


🔹 示例 3:日志上报

scss 复制代码
requestIdleCallback(() => {
  sendAnalyticsData();
});

📌 好处:浏览器空闲时再上传日志,避免影响用户体验。


3. requestAnimationFrame vs requestIdleCallback:何时用哪一个?

场景 用 requestAnimationFrame 用 requestIdleCallback
动画 ✅ 需要流畅动画 ❌ 不能保证执行时间
高频 WebSocket 数据 ✅ 控制 UI 更新频率 ❌ 可能无法及时更新
页面滚动 ✅ 监听 scroll 事件 ❌ 不能保证执行时机
后台任务(日志、数据计算) ❌ 影响 UI 体验 ✅ 浏览器空闲时执行
预加载数据 ❌ 影响用户交互 ✅ 在用户空闲时加载
AI 计算 ❌ 主线程压力大 ✅ 只有空闲时才执行

4. 如何组合使用?

🔹 WebSocket 高速数据流 + UI 更新优化

ini 复制代码
let pending = false;
let backgroundData = [];

socket.onmessage = (event) => {
  const data = JSON.parse(event.data);
  backgroundData.push(data);

  // 让 UI 更新不超过 60FPS
  if (!pending) {
    pending = true;
    requestAnimationFrame(() => {
      updateUI(backgroundData);
      backgroundData = [];
      pending = false;
    });
  }

  // 在浏览器空闲时处理日志
  requestIdleCallback(() => {
    processLogs(backgroundData);
  });
};

function updateUI(data) {
  console.log("更新 UI", data);
}

function processLogs(data) {
  console.log("后台处理数据", data);
}

📌 优化点:

  1. requestAnimationFrame 让 UI 更新流畅,不会因 WebSocket 高频推送导致页面卡死。
  2. requestIdleCallback 在浏览器空闲时处理日志,避免影响 UI 渲染。

5. 总结

  • requestAnimationFrame:UI 渲染优化 ,适用于 动画、滚动、WebSocket UI 更新。
  • requestIdleCallback:后台任务调度 ,适用于 日志、预加载、AI 计算。
  • 两者结合使用,可以优化高性能应用,提升页面流畅度和用户体验! 🚀
相关推荐
paopaokaka_luck13 小时前
宠物门店经营管理系统(销售与库存联动、进货自动记账、 退换与退款关联、员工个人业绩、ECharts 经营分析、滞销识别与客户分层、宠物档案年龄计算)
前端·javascript·spring boot·mysql·echarts·mybatis·1024程序员节
鶴哥只手遮天17 小时前
jQuery 4.0 源码架构与工程实践-第 10 章 尺寸、几何与显隐
前端·javascript·jquery
米米商聊19 小时前
图标能点,按钮就可用了吗?键盘、名称与收藏状态
前端·javascript
技术小甜甜21 小时前
【Cursor】Skill 被标黄色警告,怎么改成合格的 SKILL.md
开发语言·javascript·ai编程·cursor·1024程序员节·ai agent·skills
qetfw1 天前
Bash 函数与流程控制:read、if、for、case 验证
linux·前端·javascript·bash
鬼手点金1 天前
从 API 消息到模型 Token:Chat Template
java·前端·javascript·数据库·人工智能·深度学习·1024程序员节
Mr.Pascal1 天前
Javascript之什么是作用域?
javascript·1024程序员节
microrain1 天前
设备报文怎么变,SagooIoT 只约定三个函数名
javascript·物联网·golang·sagooiot
Bug收容所1 天前
Vue+JS Day3
前端·javascript·vue.js·前端框架·1024程序员节
小羊没烦恼!1 天前
.NET无侵入自动化探针原理和主流实现
运维·开发语言·javascript·python·自动化·.net·1024程序员节