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 计算
  • 两者结合使用,可以优化高性能应用,提升页面流畅度和用户体验! 🚀
相关推荐
像风一样自由20202 小时前
HTML与JavaScript:构建动态交互式Web页面的基石
前端·javascript·html
浪裡遊3 小时前
React Hooks全面解析:从基础到高级的实用指南
开发语言·前端·javascript·react.js·node.js·ecmascript·php
Liudef064 小时前
2048小游戏实现
javascript·css·css3
独立开阀者_FwtCoder6 小时前
【Augment】 Augment技巧之 Rewrite Prompt(重写提示) 有神奇的魔法
前端·javascript·github
我想说一句7 小时前
事件机制与委托:从冒泡捕获到高效编程的奇妙之旅
前端·javascript
汤姆Tom7 小时前
JavaScript reduce()函数详解
javascript
小飞悟7 小时前
你以为 React 的事件很简单?错了,它暗藏玄机!
前端·javascript·面试
中微子7 小时前
JavaScript 事件机制:捕获、冒泡与事件委托详解
前端·javascript
蓝翔认证10级掘手7 小时前
🤯 家人们谁懂啊!我的摸鱼脚本它...它成精了!🚀
javascript
前端康师傅8 小时前
JavaScript 中你不知道的按位运算
前端·javascript