H5新Api | requestIdleCallback - requestAnimationFram

文章目录

  • 浏览器渲染机制
    • 事件循环机制
      • 宏队列与微队列
      • 浏览器中事件循环流程
    • requestAnimationFrame(rAF)
      • requestAnimationFrame API
    • requestIdleCallback
      • requestIdleCallback API
      • 任务拆分
        • requestIdleCallback的使用场景

浏览器渲染机制

  • 每一轮 Event Loop 都会伴随着渲染吗?
  • requestAnimationFrame 在哪个阶段执行,在渲染前还是后?在 microTask 的前还是后?
    requestAnimationFrame在重新渲染屏幕之前执行
  • requestIdleCallback 在哪个阶段执行?如何去执行?在渲染前还是后?
    requestIdleCallback在渲染屏幕之后执行,并且是否有空执行要看浏览器的调度

事件循环机制

作用:事件循环机制的作用是协调事件、用户交互、脚本、渲染及网络任务等。

宏队列与微队列

一个事件循环有一个或多个宏队列,有一个微队列

一个宏队列在数据结构上是一个集合(叫做任务队列),事件循环处理模型会从选定的任务队列中获取一个可运行任务。微队列是FIFO先进先出队列。

  • 宏任务
    • setTimeout、setInterval
    • setImmediate(node 独有)
    • DOM事件、Ajax事件
    • 用户交互、用户操作事件
    • script(整体代码)
    • indexDB操作
  • 微任务
    • process.nextTick
    • Promise一些方法,如.then
    • Async/Await(实际就是promise)
    • MutationObserver(html5新特性)

浏览器中事件循环流程

在这里插入图片描述

  • 同步任务和异步任务进入不同的执行环境,同步任务放入执行栈中,异步任务放入任务队列中。
  1. 先执行同步代码
  2. 检查微任务队列,执行并清空微任务队列,如果在微任务的执行中又加入了新的微任务,也会在这一步一起执行。
  3. 进入更新渲染阶段,判断是否需要渲染(根据屏幕刷新率、页面性能等)。并不是每轮事件循环都会执行浏览器渲染
  4. 没有就开启下一轮循环,取出一个宏任务执行。一个宏任务执行完毕后就清空微队列,然后见检查需不需要。循环这个过程

DOM的修改不会立刻导致渲染,渲染线程和Javascript线程是互斥的,必须等待Javascript的这次调度执行完或线程挂起了,才能执行渲染。
这次调度可以看成是一轮事件循环完,一次事件循环=宏任务(第一次是同步代码)+微任务
在这里插入图片描述

requestAnimationFrame(rAF)

是什么

requestAnimationFrame是H5新增的API类似于setTimeout ,告诉浏览器在重新渲染屏幕之前执行。主要用途是按帧对网页进行重绘。

rAF是官方推荐的用来做一些流畅动画所应该使用的 API,做动画不可避免的会去更改 DOM,而如果在渲染之后再去更改 DOM,那就只能等到下一轮渲染机会的时候才能去绘制出来了

优势
调用时机:在重新渲染前调用。
requestAnimationFrame最大的优势是由系统来决定回调函数的执行时机。保证回调函数在屏幕每一次刷新间隔中只执行一次,避免丢帧

如果浏览器不渲染,是不是就不会调用requestAnimationFrame? 如果requestAnimationFrame做太多事情,会导致降频,比如1s刷新60次变成1s刷新30次。

requestAnimationFrame API

基本语法:requestAnimationFrame (callback)
返回值:回调函数列表中的唯一值,可以使用cancelAnimationFrame传入请求ID取消回调函数。

说明

  1. requestAnimationFrame不管理回调函数,意思是多次调用带有同一回调函数的 requestAnimationFrame,会导致回调在同一帧中执行多次。
    由rAF的返回值是回调函数列表中的唯一值,可以理解为即使是同一回调函数,但是在回调函数列表中的值都是不一样的。
    所以配合cancelAnimationFrame使用。
  2. requestAnimationFrame() 运行在后台标签页或者隐藏的<iframe> 里时,requestAnimationFrame() 会被暂停调用以提升性能和电池寿命。

requestIdleCallback

对于人眼来说,当每秒切换60张图片时,就会认为是连贯的。所以主流的显示器是60hz的(1s刷新60次),那么每16.7ms需要刷新一次,浏览器会自动适配这个频率,这时对应前端页面就是每16.7ms需要渲染一次。
在这里插入图片描述

页面每隔16.7ms才会渲染一次,那么在两次渲染的中间时间,就是浏览器的空闲时间,在这段空闲时间执行的任务,是不会阻塞到页面渲染的流畅性的。

如果在某一帧区间内执行过多的任务会导致下一帧一直没办法渲染,页面看起来就被卡住。

对大量任务的计算首先考虑Web Worker 使其不占用主线程,如果需要操作DOM,可以考虑任务拆分。

在这里插入图片描述
图中一帧包括了用户的交互, JavaScript 脚本执行; 以及requestAnimationFrame(rAF)的调用, 布局计算以及页面重绘等。如果某一帧里执行的任务不多, 在不到 16.66ms内就完成了上述任务, 那么这一帧就会有一定空闲时间来执行requestIdleCallback的回调。会在layout/paint 之前调用。

回流也叫重排(layout),当 DOM 的变化影响了元素的几何信息(位置、尺寸大小等),浏览器需要重新计算元素的几何属性,将其安放在界面的正确位置,这个过程叫做回流。
当一个元素的外观发生变化,但没有改变布局,重新把元素外观绘制出来的过程,叫做重绘(paint)。

requestIdleCallback API

基本语法:requestIdleCallback(callback,options)

  • callback被调用时,回接受一个参数 deadlinedeadline是一个对象,对象上有两个属性
    • timeRemaining属性是一个函数,函数的返回值表示当前空闲时间还剩下多少时间
    • didTimeoutdidTimeout属性是一个布尔值,如果didTimeouttrue,那么表示本次callback的执行是因为超时的原因
  • options是一个对象,可以用来配置超时时间。如果指定了timeout,但是浏览器没有在timeout指定的时间内,执行callback。在下次空闲时间时,callback会强制执行。并且callback的参数,deadline.didTimeout=true, deadline.timeRemaining()返回0。

空闲时间
在空闲期间,callback的执行顺序是以FIFO(先进先出)的顺序。但是如果在空闲时间内依次执行callback时,有一个callback的执行时间,已经将空闲时间用完了,剩下的callback将会在下一次的空闲时间执行。
在这里插入图片描述

const startTask = (deadline) {
    // 如果 `task` 花费的时间是20ms
    // 超过了当前空闲时间的剩余毫秒数,我们等到下一次空闲时间执行task
    if (deadline.timeRemaining() <= 20) {
        // 将任务带到下一个空闲时间周期内
        // 添加到下一个空闲时间周期callback列表的末尾
        requestIdleCallback(startTask)
    } else {
        // 执行任务
        task()
    }
}

当网页处于不可见的状态时(比如切换到其他的tag),空闲时间将会每10s, 触发一次空闲期。

任务拆分

将批量的任务进行拆分,保证这些任务只在空闲时间执行。每次执行下一个任务时,先检查当前页面是否该渲染下一帧了,如果是则让出线程,进行页面渲染。
requestIdleCallback是浏览器提供给我们用来判断这个时间的api,它会在浏览器空闲的时候来执行其回调函数,如果指定了超时时间,会在超时后的下一帧强制执行。

const id = window.requestIdleCallback((deadline) => {
  // 当前帧剩余时间大于0,或任务已超时
  if(deadline.timeRemaining() > 0 || deadline.didTimeout) {
      // do something
      console.log(1)
  }
}, { timeout: 2000 }) // 指定超时时间

// window.cancelIdleCallback(id) 与定时器类似,支持取消

requestIdleCallback在Event Loop的执行时机如下图所示,蓝色区域代表一帧内的渲染任务,当这些任务执行完后,剩余的时间被认为是空闲时间。
在这里插入图片描述
使用案例

// class中的一个方法
idleDownload(){
	// 先取消之前的
	cancelIdleCallback(this.ridId); // ridId是class中的属性,存放一个requestIdleCallback的id
	const { tasks } = this // tasks为总任务数
	let index = 0; // 任务索引
	const ridOption = {timeout:2000}; // 指定超时时间,会在超时后的下一帧强制执行。
	// 当前帧空闲时执行的回调函数
	const handler = (idleDeadline) => {
		const {timeRemaining} = idleDeadline; // 获取空闲时间
		while(timeRemaining()>0 && index<tasks.length ){
			// 在空闲时间执行任务
			index ++;
		}
		// 判断任务是否下载完成
		if(index< tasks.length){ // 不空闲了,但是任务还没有执行完毕
			this.ridId = requestIdleCallback(handler, ridOption); // 继续等待下次空闲时下载
		}else{ 
		// 已经下载完毕
		}
	}
	this.ridId =  requestIdleCallback(handler, ridOption); 
}
requestIdleCallback的使用场景

适用场景

  1. 预加载
  2. 检测卡顿
    如果requestIdleCallback长时间内没能得到执行,说明一直没有空闲时间,很有可能就是发生了卡顿,从而可以打点上报。它比较适用于行为卡顿,举个例子:点击某个按钮并同时添加requestIdleCallback 回调,如果点击后的一段时间内这个回调没有得到执行,很大概率是这个点击操作造成了卡顿。
  3. 拆分耗时任务

不适用场景

  • 更新DOM操作
    requestIdleCallback回调执行之前, 样式变更以及布局计算等都已经完成。如果在callback中修改DOM, 之前所作的布局计算都会失效。 并且如果下一帧里有获取布局相关的操作, 浏览器就需要强制进行重排, 极大的影响性能。 另外由于修改 DOM 的时间是不可预测的, 因此容易超过当前帧空闲的阈值.
  • promise 的回调(resolve/reject)属于优先级较高任务,在一帧的过程中如果产生了微任务会执行微任务。所以会在 requestIdleCallback 回调结束后立即执行,可能会给这一帧带来超时的风险。
// console
// 空闲时间1
// 等待了1000ms
// 空闲时间2
// Promise 会在空闲时间1接受后立即执行,即使没有空闲时间了也是如此。拖延了进入下一帧的时间

requestIdleCallback(() => {
    console.log('空闲时间1')
    Promise.resolve().then(() => {
        sleep(1000)
        console.log('等待了1000ms')
    })
})

requestIdleCallback(() => {
    console.log('空闲时间2')
})

参考文章:
批量任务导致页面卡死?试试requestIdleCallback对任务进行拆分
详解 requestIdleCallback
requestAnimationFrame 执行机制探索

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Mac/Linux安装使用 opengauss数据库步骤

问题背景 一般部署opengauss数据库在虚拟机中&#xff0c;Mac使用虚拟机步骤较为繁琐&#xff0c;可以使用Docker部署opengauss数据库。Linux也可以使用此方式来部署opengauss数据库。 1. 在docker官网下载Docker桌面版&#xff0c;m系列芯片选Apple Chip。如果是Linux就下载…

vue数组中的变更方法和替换方法

变更方法&#xff1a; Vue 能够侦听响应式数组的变更方法&#xff0c;并在它们被调用时触发相关的更新。这些变更方法包括: push&#xff08;&#xff09;:在数组末尾添加一个或者多个元素&#xff0c;返回新的长度。 var arr [1, 2, 3, 4, 5]; // 定义一个数组 arr.push(6…

python sqlalchemy(ORM)- 02 表关系

文章目录 创建连接事务和DBAPIORM 操作表关系ORM表示 1v1ORM表示 1vm 创建连接 sqlalchemy应用必须创建一个engine&#xff0c;用于连接数据库&#xff1b; from sqlalchemy import create_engine # 创建engine, 懒连接&#xff0c;在ORM中由Session管理 engine create_engi…

第二证券:AIGC概念活跃,焦点科技、三维通信涨停,万兴科技大涨

AIGC概念24日盘中走势生动&#xff0c;到发稿&#xff0c;万兴科技、三态股份涨超10%&#xff0c;焦点科技、三维通讯、我国科传等涨停&#xff0c;中文在线涨超9%&#xff0c;果麦文明、新国都涨约7%。 消息面上&#xff0c;各大电商途径于10月18-24日先后发动“双11”大促或…

国产CAN总线收发芯片DP1042 兼容替换TJA1042

说明 1 简述 DP1042是一款应用于 CAN 协议控制器和物理总线之间的接口芯片&#xff0c;可应用于卡车、公交、小汽车、工业控制等领域&#xff0c;支持 5Mbps CAN FD 灵活数据速率&#xff0c;具有在总线与 CAN 协议控制器之间进行差分信号传输的能力&#xff0c;完全兼容“ISO…

C指针 --- 进阶

目录 1. 字符指针 1.1. 一般使用 1.2. 另一种使用 2. 指针数组 3. 数组指针 3.1. 数组指针 3.2. 数组名和&数组名 3.3. 数组指针的用处 1. 传递一个数组 2. 传递数组首元素的地址 3. 数组指针处理一维数组 4. 数组指针处理二维数组 4. 数组传参和指针传参 4.1…

全连接层是什么,有什么作用?

大家好啊&#xff0c;我是董董灿。 如果你是搞AI算法的同学&#xff0c;相信你在很多地方都见过全连接层。 无论是处理图片的卷积神经网络&#xff08;CNN&#xff09;&#xff0c;还是处理文本的自然语言处理&#xff08;NLP&#xff09;网络&#xff0c;在网络的结尾做分类…

2023.10.26-SQL测试题

employee表&#xff1a; department表&#xff1a; job表&#xff1a; location表&#xff1a; 题目及答案&#xff1a; -- (1).查询工资大于一万的员工的姓名(first_name与last_name用“.”进行连接)和工资-- select CONCAT(first_name,.,last_name) as 姓名 ,salary -…

Vue(uniapp)父组件方法和子组件方法执行优先顺序

涉及到的知识点&#xff1a;watch监控&#xff1a;先看问题&#xff0c;父组件从后端通过$ajax获取数据&#xff0c;在将父组件将值传输给子组件&#xff0c;使用子组件使用created钩子函数获取数据&#xff0c;按自己的想法应该是父组件先获取后端数据&#xff0c;在传入给子组…

引入个性化标签的协同过滤推荐算法研究_邢瑜航

第3章 引入个性化标签的I-CF推荐算法 3.2.2 相似性度量方法 3.2.3 改进后的算法步骤与流程

Python:一个函数可以被多个装饰器装饰

理解&#xff1a; 规律&#xff1a; 一个函数可以被多个装饰器装饰. wrapper1 wrapper2 def target():print(我是目标)规则和规律 wrapper1 wrapper2 TARGET wrapper2 wrapper1def wrapper1(fn): # fn: wrapper2.innerdef inner(*args, **kwargs):print("这里是wrapper1 …

【RabbitMQ 实战】12 镜像队列

一、镜像队列的概念 RabbitMQ的镜像队列是将消息副本存储在一组节点上&#xff0c;以提高可用性和可靠性。镜像队列将队列中的消息复制到一个或多个其他节点上&#xff0c;并使这些节点上的队列保持同步。当一个节点失败时&#xff0c;其他节点上的队列不受影响&#xff0c;因…

【网络协议】聊聊TCP的三挥四握

上一篇我们说了网络其实是不稳定的&#xff0c;TCP和UDP其实是两个不同的对立者&#xff0c;所以TCP为了保证数据在网络中传输的可靠性&#xff0c;从丢包、乱序、重传、拥塞等场景有自己的一套打法。 TCP格式 源端口和目标端口是不可缺少的&#xff0c;用以区分到达发送给拿…

【每日一题】掷骰子等于目标和的方法数

文章目录 Tag题目来源题目解读解题思路方法一&#xff1a;动态规划 写在最后 Tag 【动态规划】【数组】 题目来源 1155. 掷骰子等于目标和的方法数 题目解读 你手里有 n 个一样的骰子&#xff0c;每个骰子都有 k 个面&#xff0c;分别标号 1 到 n。给定三个整数 n&#xff0…

java异常处理

异常处理分为三类&#xff1a; 检查性异常 用户错误或问题引起的异常&#xff0c;这是程序员无法预见的。例如要打开一个不存在文件时&#xff0c;一个异常就发生了&#xff0c;这些异常在编译时不能被简单地忽略。 运行时异常 运行时异常是可能被程序员避免的异常&#xf…

2023深耕kotlin,谈谈前景

为什么学习kotlin&#xff1f; Kotlin 早就已经是 Google 官方推荐的开发语言了&#xff0c;而且 Android 新的 Compose 框架只支持 Kotlin &#xff0c;在 Google 那里&#xff0c;Android开发中 Java 其实已经被淘汰了。Java 和 Kotlin 虽然都属于高级语言&#xff0c;但是 …

LeetCode--2.两数相加

文章目录 1 题目描述2 解题思路2.1 代码实现 1 题目描述 给你两个 非空 的链表, 表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的, 并且每个节点只能存储 一位 数字 请你将两个数相加, 并以相同形式返回一个表示和的链表 你可以假设除了数字 0 之外, 这两个数都…

redis archive github

https://github.com/redis/redis/releases/tag/7.2.2https://github.com/redis/redis/releases/tag/7.2.2

虹科分享 | 买车无忧?AR带来全新体验!

文章来源&#xff1a;虹科数字化与AR 阅读原文&#xff1a;https://mp.weixin.qq.com/s/XsUFCTsiI4bkEMBHcGUT7w 新能源汽车的蓬勃发展&#xff0c;推动着汽车行业加速进行数字化变革。据数据显示&#xff0c;全球新能源汽车销售额持续上升&#xff0c;预计到2025年&#xff0…

VTK OrientationMarker 方向 三维坐标系 相机坐标轴 自定义坐标轴

本文 以 Python 语言开发 我们在做三维软件开发时&#xff0c;经常会用到相机坐标轴&#xff0c;来指示当前空间位置&#xff1b; 坐标轴效果&#xff1a; 相机方向坐标轴 Cube 正方体坐标轴 自定义坐标轴&#xff1a; Code&#xff1a; Axes def main():colors vtkNamedC…