深入理解 requestIdleCallback:浏览器空闲时段的性能优化利器

requestIdleCallback 核心作用

requestIdleCallback 是浏览器提供的 API,用于将非关键任务延迟到浏览器空闲时段执行,避免阻塞用户交互、动画等关键任务,从而提升页面性能体验。

基本语法

const handle = window.requestIdleCallback(callback[, options])

参数

  • callback:一个将在浏览器空闲时期被调用的函数。该回调函数接收一个参数:

    • IdleDeadline 对象,包含:

      • timeRemaining():返回当前帧剩余的空闲时间(毫秒),通常 ≤ 50ms

      • didTimeout:布尔值,表示是否因为指定的 timeout 时间已到而触发回调

  • options(可选):配置对象

    • timeout:如果指定了 timeout,并且回调在 timeout 毫秒后还没有被调用,则回调会在下一次有机会时被强制执行

返回值

返回一个 ID,可以传递给 cancelIdleCallback() 来取消回调。

配套方法

window.cancelIdleCallback(handle)

取消之前通过 requestIdleCallback() 安排的回调。 

工作原理

  1. 浏览器在每一帧渲染完成后会检查是否有空闲时间

  2. 如果有空闲时间,且存在待执行的 idle 回调,则执行它们

  3. 每次 idle 回调执行时,可以通过 timeRemaining() 检查剩余时间

  4. 如果任务未完成,可以在回调中再次调用 requestIdleCallback 继续处理

使用示例

基本用法

function processInIdleTime(deadline) {while (deadline.timeRemaining() > 0 && tasks.length > 0) {performTask(tasks.pop());}if (tasks.length > 0) {requestIdleCallback(processInIdleTime);}
}requestIdleCallback(processInIdleTime);

带超时的用法

requestIdleCallback(processInIdleTime, { timeout: 2000 });
// 保证在2秒内执行,即使浏览器一直不空闲

关键特性

特性说明
空闲期执行只在浏览器主线程空闲时运行(每帧渲染后的空闲时间)
可中断性如果用户开始交互,任务会被暂停
超时控制可通过 timeout 参数强制在指定时间后执行(避免长期等待)

适用场景

  1. 日志上报和分析:将非关键的日志发送推迟到空闲时间

  2. 预加载资源:预加载接下来可能需要的非关键资源

  3. 大数据处理:分块处理大型数据集,避免界面卡顿

  4. 非关键UI更新:如更新界面上的辅助信息或统计数字

注意事项

  1. 不要用于关键任务:空闲回调可能永远不会执行,或者执行得很晚

  2. 任务应该可分片:每次回调应该只处理一小部分工作

  3. 避免DOM操作:在空闲回调中进行DOM操作可能触发重排/重绘

  4. 超时设置要合理:过短的 timeout 会使 API 失去意义,过长则影响体验

浏览器兼容性分析

✅ 完全支持的浏览器
  • Chrome

    • 版本:47+(2015年发布)

    • 备注:包括所有基于 Chromium 的浏览器(Edge、Opera 等)

  • Firefox

    • 版本:55+(2017年发布)

    • 备注:在移动端和桌面端表现一致

  • Edge

    • 版本:79+(Chromium 内核版本)

⚠️ 部分支持/行为差异的浏览器
  • Safari

    • 版本:部分支持(需检测)

    • 问题:

      • iOS Safari 和 macOS Safari 实现可能不一致

      • 某些版本中 timeRemaining() 返回值不准确

❌ 不支持的浏览器
  • Internet Explorer

    • 所有版本均不支持

  • 旧版 Edge(EdgeHTML 内核)

    • 版本:18 及以下

  • Android 默认浏览器(4.4及以下)


兼容性风险点列表

  1. 移动端注意

    • 部分安卓 WebView(特别是 Hybrid 应用内嵌浏览器)可能不支持

  2. Safari 特殊性

    • 某些版本即使支持 API,空闲时间计算可能不准确

  3. 隐身模式影响

    • 部分浏览器在隐身模式下会限制后台任务执行

兼容性解决方案列表

特性检测标准写法

const hasIdleCallback = 'requestIdleCallback' in window;

推荐降级方案

优先降级到 requestAnimationFrame(适合视觉相关任务)

其次降级到 setTimeout(callback, 0)(通用方案)

Polyfill 选择

官方推荐的 polyfill

注意:polyfill 无法真正模拟空闲期,只是延迟执行

/*** 增强型空闲任务调度器(支持多级降级方案)* @param {Function} callback - 需要执行的回调函数,接收 deadline 对象* @param {Object} [options] - 配置选项* @param {number} [options.timeout=0] - 超时时间(毫秒)* @returns {number} 调度器ID(可用于取消)*/
function enhancedRequestIdleCallback(callback, options = {}) {// 参数有效性检查if (typeof callback !== 'function') {throw new TypeError('回调必须是函数');}const { timeout = 0 } = options;// 原生支持检测if ('requestIdleCallback' in window) {return window.requestIdleCallback(callback, { timeout });}// ========== 降级方案实现 ==========let id;const start = Date.now();const isVisualTask = isRelatedToVisualUpdate(callback);// 方案1:视觉相关任务使用 requestAnimationFrameif (isVisualTask && 'requestAnimationFrame' in window) {id = window.requestAnimationFrame(() => {callback({timeRemaining: () => Math.max(0, 16.6 - (Date.now() - start)),didTimeout: Date.now() - start >= timeout});});}// 方案2:通用任务使用 setTimeoutelse {// 计算合理延迟时间(避免过度消耗资源)const delay = calculateSafeDelay(isVisualTask);id = window.setTimeout(() => {callback({timeRemaining: () => 1, // 模拟1ms剩余时间didTimeout: true       // 降级模式下总是触发超时});}, delay);}// 添加超时强制触发机制if (timeout > 0) {const timeoutId = setTimeout(() => {callback({timeRemaining: () => 0,didTimeout: true});clearTimeout(id);}, timeout);// 返回复合ID用于取消return { rId: id, tId: timeoutId };}return id;
}/*** 取消空闲任务调度* @param {number|Object} id - 调度器返回的ID*/
function enhancedCancelIdleCallback(id) {if ('cancelIdleCallback' in window) {window.cancelIdleCallback(id);return;}// 处理复合ID(超时场景)if (typeof id === 'object') {clearTimeout(id.tId);id = id.rId;}// 根据降级方案取消if ('cancelAnimationFrame' in window) {window.cancelAnimationFrame(id);} else {clearTimeout(id);}
}// ========== 工具函数 ==========
/*** 判断任务是否与视觉更新相关* (根据常见DOM API使用模式推测)*/
function isRelatedToVisualUpdate(fn) {const fnStr = fn.toString();return /(offset|scroll|client|getBounding|style)/.test(fnStr);
}/*** 计算安全延迟时间* 视觉任务:下一帧时间(16.6ms)* 非视觉任务:分级延迟(0-50ms随机)*/
function calculateSafeDelay(isVisual) {return isVisual ? 16 : Math.min(50, Math.floor(Math.random() * 50));
}// ========== 使用示例 ==========
// 示例任务
function backgroundTask(deadline) {while (deadline.timeRemaining() > 0) {// 执行任务分片...}if (hasMoreWork) {enhancedRequestIdleCallback(backgroundTask);}
}// 启动任务
const taskId = enhancedRequestIdleCallback(backgroundTask, { timeout: 2000 });// 取消任务
// enhancedCancelIdleCallback(taskId);

注意事项

避免在回调中修改 DOM(可能触发重排)

空闲时间不保证,任务应有中断/恢复机制

耗时任务应使用 Web Worker

requestIdleCallback的详细介绍和示例代码

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>requestIdleCallback 示例与兼容性处理</title>
</head>
<body>
<h1>requestIdleCallback 演示</h1>
<div id="output"></div><script>/*** 兼容性处理:如果原生不支持 requestIdleCallback,* 使用 setTimeout 实现降级方案*/window.requestIdleCallback = window.requestIdleCallback || function(cb) {// 降级方案:用 50ms 延迟模拟空闲时段let start = Date.now();return setTimeout(function() {cb({didTimeout: false,timeRemaining: function() {// 确保至少留出 1ms 时间return Math.max(0, 50 - (Date.now() - start));}});}, 1);};/*** 分块任务处理器* @param {Array} taskList - 要处理的任务数组* @param {Function} processor - 单个任务处理函数* @param {number} chunkSize - 每次处理的任务数(默认 10)*/function processTasksInIdle(taskList, processor, chunkSize = 10) {let index = 0;function doChunk(deadline) {// 当剩余时间 > 0 或超时前处理任务while ((deadline.timeRemaining() > 0 || deadline.didTimeout) &&index < taskList.length) {// 每次处理指定数量的任务const tasksToProcess = taskList.slice(index, index + chunkSize);tasksToProcess.forEach(task => processor(task));index += chunkSize;// 更新页面显示进度updateProgress(index);}// 如果还有剩余任务,继续调度if (index < taskList.length) {// 使用超时参数 100ms 保证即使不空闲也会执行requestIdleCallback(doChunk, { timeout: 100 });}}// 初始调用requestIdleCallback(doChunk, { timeout: 100 });}// 示例:创建 500 个元素的列表(模拟大量任务)const dummyTasks = new Array(500).fill(null).map((_, i) => ({id: i + 1,content: `Item ${i + 1}`}));// 任务处理函数(模拟DOM操作)function handleTask(task) {const div = document.createElement('div');div.textContent = task.content;// 这里可以添加更复杂的操作}// 更新进度显示function updateProgress(processedCount) {const output = document.getElementById('output');output.textContent = `已处理 ${processedCount}/${dummyTasks.length} 项任务`;}// 启动任务处理(页面加载完成后)window.addEventListener('load', () => {processTasksInIdle(dummyTasks, handleTask, 10); // 每次处理10个});
</script><!-- 兼容性提示 -->
<script>// 检测是否原生支持if (!window.requestIdleCallback) {const warn = document.createElement('p');warn.style.color = 'red';warn.textContent = '当前浏览器不支持 requestIdleCallback,已使用 setTimeout 降级方案';document.body.appendChild(warn);}
</script>
</body>
</html>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/81445.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

51单片机——交通指示灯控制器设计

设计目标 1、设计一交通灯控制&#xff0c;控制东西方向的红、黄、绿灯和南北方向的红、黄、绿灯。 2、可手动控制和自动控制&#xff0c;设置两个输入控制开关。 手动/自动开关&#xff0c;通过P11的按键输入控制 3、手动&#xff1a;设置开关P11&#xff0c;两种情况&#x…

神经网络语言模型(前馈神经网络语言模型)

神经网络语言模型 什么是神经网络&#xff1f;神经网络的基本结构是什么&#xff1f;输入层隐藏层输出层 神经网络为什么能解决问题&#xff1f;通用近似定理为什么需要权重和偏置&#xff1f;为什么需要激活函数&#xff1f;权重是如何确定的&#xff1f;1. 穷举2. 反向传播主…

信息系统项目管理师高级-软考高项案例分析备考指南(2023年案例分析)

个人笔记整理---仅供参考 计算题 案例分析里的计算题就是进度、挣值分析、预测技术。主要考査的知识点有:找关键路径、求总工期、自由时差、总时差、进度压缩资源平滑、挣值计算、预测计算。计算题是一定要拿下的&#xff0c;做计算题要保持头脑清晰&#xff0c;认真读题把PV、…

unordered_map和unordered的介绍和使用

目录 unordered系列关联式容器 unordered_map unordered_map的接口说明 unordered_map的定义方式 unordered_map接口的使用 unordered_map的容量 unordered_map的迭代器 unordered_map的元素访问 unordered_map的查询 unordered_map的修改操作 unordered_multimap u…

设计模式7大原则与UML类图详解

设计模式7大原则与UML类图详解 引言 &#x1f31f; 在软件工程领域&#xff0c;设计模式和UML&#xff08;统一建模语言&#xff09;是提高代码质量、增强系统可维护性的重要工具。设计模式提供了解决软件设计中常见问题的通用方案&#xff0c;而UML则为我们提供了一种可视化的…

计算机视觉与深度学习 | Python实现ARIMA-LSTM时间序列预测(完整源码和数据)

ARIMA-LSTM混合模型 1. 环境准备2. 数据生成(示例数据)3. 数据预处理4. ARIMA建模5. LSTM残差建模6. 混合预测7. 结果可视化完整代码说明1. **数据生成**2. **ARIMA建模**3. **LSTM残差建模**4. **混合预测**5. **性能评估**参数调优建议扩展方向典型输出以下是使用Python实现…

Docker部署单节点Elasticsearch

1.Docker部署单节点ES 1.前置条件 配置内核参数 echo "vm.max_map_count262144" >> /etc/sysctl.conf sysctl -w vm.max_map_count262144准备密码 本文所有涉及密码的配置&#xff0c;均使用通用密码 Zzwl2024。 生产环境&#xff0c;请用密码生成器生成20…

pe文件二进制解析(用c/c++解析一个二进制pe文件)

pe文件二进制解析 c解析pe文件控制台版本 #include<iostream> #include<windows.h> #include<vector>/*RVA&#xff08;相对虚拟地址&#xff09;与FOA&#xff08;文件偏移地址&#xff09;的转换1.得到 的值&#xff1a;内存地址 - ImageBase2.判断是否位…

融智学视域下的系统性认知增强框架——基于文理工三类AI助理赋能HI四阶跃迁路径

融智学视域下的系统性认知增强框架 ——基于文理工三类AI助理赋能HI四阶跃迁路径 一、如何排除50个认知偏差&#xff1a;消除50类偏差的精准矫正系统 1. 技术架构 文科AI&#xff1a; 构建文化语义场&#xff08;Cultural Semantic Field, CSF&#xff09;&#xff0c;通过…

MMDetection环境安装配置

MMDetection 支持在 Linux&#xff0c;Windows 和 macOS 上运行。它需要 Python 3.7 以上&#xff0c;CUDA 9.2 以上和 PyTorch 1.8 及其以上。 MMDetection 至今也一直更新很多个版本了&#xff0c;但是对于最新的pytorch版本仍然不支持&#xff0c;我安装的时候仍然多次遇到m…

如何实现k8s高可用

一、控制平面高可用设计 多主节点部署 • API Server 冗余&#xff1a;部署至少 3 个 Master 节点&#xff0c;每个节点运行独立的 API Server&#xff0c;通过负载均衡器&#xff08;如 Nginx、HAProxy、云厂商 LB&#xff09;对外提供统一入口。 • 选举机制&#xff1a;Sche…

记录心态和工作变化

忙中带闲的工作 其实工作挺忙的, 总是在赶各种功能点. 好巧的是iOS那边因为上架的问题耽搁了一些时间, 从而让Android的进度有了很大的调整空间. 更巧的是后端那边因为对客户端的需求不是很熟悉, 加上Android海外这块的业务他也是第一次接触. 所以需要给他留一些时间把各个环节…

JVM 双亲委派机制

一、从 JDK 到 JVM&#xff1a;Java 运行环境的基石 在 Java 开发领域&#xff0c;JDK&#xff08;Java Development Kit&#xff09;是开发者的核心工具包。它不仅包含了编译 Java 代码的工具&#xff08;如 javac&#xff09;&#xff0c;还内置了 JRE&#xff08;Java Run…

java开发之异常

一 结构 Throwable分为Exception和error Exception分为RuntimeException&#xff08;运行时异常&#xff09;和其他异常 主动抛出运行时异常和非运行时异常的区别 1、throw RuntimeException&#xff08;或运行时异常的子类&#xff09; 编译时不会报错。 2、throw Excepti…

MySQL 中 JOIN 和子查询的区别与使用场景

目录 一、JOIN:表连接1.1 INNER JOIN:内连接1.2 LEFT JOIN:左连接1.3 RIGHT JOIN:右连接1.4 FULL JOIN:全连接二、子查询:嵌套查询2.1 WHERE 子句中的子查询2.2 FROM 子句中的子查询2.3 SELECT 子句中的子查询三、JOIN 和子查询的区别3.1 功能差异3.2 性能差异3.3 使用场…

2025年第三届盘古石杯初赛(智能冰箱,监控部分)

前言 所以去哪里可以取到自己家里的智能家居数据呢&#xff1f;&#xff1f;&#xff1f;&#xff1f; IOT物联网取证 1、分析冰箱&#xff0c;请问智能冰箱的品牌&#xff1f; [答案格式&#xff1a;xiaomi] Panasonic2、请问智能冰箱的型号&#xff1f; [答案格式&#x…

【强化学习】强化学习算法 - 马尔可夫决策过程

文章目录 马尔可夫决策过程 (Markov Decision Process, MDP)1. MDP 原理介绍2. MDP 建模/实现步骤3. MDP 示例&#xff1a;简单网格世界 (Grid World) 马尔可夫决策过程 (Markov Decision Process, MDP) 1. MDP 原理介绍 马尔可夫决策过程 (MDP) 是强化学习 (Reinforcement L…

用户现场不支持路由映射,如何快速将安防监控EasyCVR视频汇聚平台映射到公网?

一、方案背景​ 随着数字化安防与智能交通管理发展&#xff0c;视频监控远程管理需求激增。EasyCVR作为专业视频融合平台&#xff0c;具备多协议接入等核心功能&#xff0c;是智能监控的重要工具。但实际部署中&#xff0c;当EasyCVR处于内网且路由器无法进行端口映射时&#…

MODBUS RTU调试助手使用方法详解

一、软件简介 485调试助手是一款常用的串口通信调试工具&#xff0c;专门用于RS-485总线设备的测试、调试和通信监控。它支持多种串口参数设置&#xff0c;提供数据收发功能&#xff0c;是工业现场调试的必备工具之一。 二、软件安装与启动 1. 系统要求 Windows 7/10/11操作…

ECMAScript 2018(ES2018):异步编程与正则表达式的深度进化

1.版本背景与发布 发布时间&#xff1a;2018年6月&#xff0c;由ECMA International正式发布&#xff0c;标准编号为ECMA-262 9th Edition。历史意义&#xff1a;作为ES6之后的第三次年度更新&#xff0c;ES2018聚焦于异步编程、正则表达式和对象操作的标准化&#xff0c;推动…