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

深入解析 requestAnimationFramerequestIdleCallback


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. 总结

  • requestAnimationFrameUI 渲染优化 ,适用于 动画、滚动、WebSocket UI 更新
  • requestIdleCallback后台任务调度 ,适用于 日志、预加载、AI 计算
  • 两者结合使用,可以优化高性能应用,提升页面流畅度和用户体验! 🚀
相关推荐
aklry17 分钟前
原型与原型链
javascript
头发秃头小宝贝39 分钟前
深入理解 JavaScript 中的 call 方法实现
前端·javascript·面试
海底火旺41 分钟前
走进 JavaScript 的面向对象世界:构造函数与原型探秘
javascript
MiyueFE1 小时前
bpmn-js 源码篇8:Featrues 体验优化与功能扩展(三)
前端·javascript
dchen772 小时前
xhr和fetch的一些区别对比
前端·javascript·面试
徐小夕2 小时前
耗资100小时,我开源了一款PPT在线编辑器
前端·javascript·github
Vae_Mars2 小时前
uniapp中props的用法
前端·javascript·uni-app
百事可乐☆2 小时前
vue 对接 paypal 订阅和支付
前端·javascript·vue.js
SUN14862011818802 小时前
JavaScript编码规范
javascript·代码规范