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

news/2025/3/26 4:00:15/

深入解析 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:流畅动画

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 高频推送数据

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 优化:

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:处理后台任务

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

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


🔹 示例 2:懒加载数据

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

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

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


🔹 示例 3:日志上报

requestIdleCallback(() => {sendAnalyticsData();
});

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


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

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

4. 如何组合使用?

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

let pending = false;
let backgroundData = [];socket.onmessage = (event) => {const data = JSON.parse(event.data);backgroundData.push(data);// 让 UI 更新不超过 60FPSif (!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 计算
  • 两者结合使用,可以优化高性能应用,提升页面流畅度和用户体验! 🚀

http://www.ppmy.cn/news/1583128.html

相关文章

如何使用go的template模版

tmpl, err tmpl.New("page_content").Parse(fmt.Sprintf({{template "%s" .}}, contentBlockName)) 创建新块&#xff1a; tmpl.New("page_content")&#xff1a;在模板对象tmpl中定义一个新的、名为"page_content"的块。这个块是动…

深入剖析Java虚拟机(JVM):从零开始掌握Java核心引擎

&#x1f4cc; 引言&#xff1a;为什么每个Java开发者都要懂JVM&#xff1f; 想象你是一名赛车手&#xff0c;Java是你的赛车&#xff0c;而JVM就是赛车的引擎。 虽然你可以不关心引擎内部构造就能开车&#xff0c;但要想在比赛中获胜&#xff0c;必须了解引擎如何工作&#…

3:库的增删查改,编码,备份恢复

1. 数据库增删查改&#xff1a; show databases; //展示数据库 create database xxx; //创建数据库xxx&#xff0c;本质在var/lib/mysql下创建一个xxx目录 drop database xxx; // 删除数据库xxx&#xff0c;本质在var/lib/mysql下删除xxx目录 create database xxx charsetutf8…

线程池实现学习笔记1

线程池实现学习笔记 今天花了一些时间学习和实现了线程池&#xff0c;收获颇丰。在这里记录一下自己的学习心得&#xff0c;希望对大家也有帮助。 为什么需要线程池&#xff1f; 在实际开发中&#xff0c;如果每个任务都创建一个新线程&#xff0c;当任务数量很大时会带来以…

SQL Optimization

SQL Optimization &#xff08;SQL 优化&#xff09; 1) * && field SELECT * from sys_user SELECT USER_ID, USER_NAME, EMAIL FROM SYS_USER; 栗子&#xff1a; 48.664s 142877rows 6.194s 142877rows 2&#xff09;UNION && UNION ALL …

单链表的查找和插入,删除操作

1.单链表的查找 snode* slistfind(snode* stlheap, stltype x) {while (stlheap){if (stlheap->data x){return stlheap;}stlheap stlheap->next;}return NULL; } 2.单链表的插入操作 2.1在指定位置之前插入节点 void slistinsert(snode** stlheap, snode* pos, stl…

Linux:基础IO---文件描述符

文章目录 1. 前言1.1 C语言文件知识回顾 2. 文件2.1 文件基础知识 3. 被打开的文件3.1 以C语言为主&#xff0c;先回忆一下C文件接口3.2 过渡到系统&#xff0c;认识文件系统调用3.3 访问文件的本质3.4 重定向&&缓冲区 序&#xff1a;在深入了解了进程的内容后&#xf…

游戏引擎学习第172天

总结今天的计划 这次的项目我们没有使用任何游戏引擎或者第三方库&#xff0c;而是完全自己动手编写。这种方式可能没有经济效益&#xff0c;但我认为每个人都应该有一次亲身经历&#xff0c;了解开发一款游戏时所涉及的所有内容。这样能让开发者更加灵活&#xff0c;能够做很…