requestIdleCallback解决页面卡顿

一、基本概念

  1. js是单线程执行的,分为GUI渲染线程,js引擎线程,事件触发线程,定时器触发器线程,http请求线程。
  2. 执行js会阻塞gui渲染,如js执行所需时间超过一帧的时间,就会导致页面掉帧,也就是肉眼看到的卡顿。

二、场景模拟

1. 准备工作

通过一个小方块平移运动模拟浏览器渲染图形

css 复制代码
.block {
    position: absolute;
    left: 100px;
    top: 100px;
    width: 50px;
    height: 50px;
    background: #f00;
    animation: move 3s linear infinite alternate;
}

@keyframes move {
    0% {
        left: 100px;
    }
    100% {
        left: 300px;
    }
}

使用while模拟js阻塞

js 复制代码
function task() {
    const now = performance.now()
    while(performance.now() - now < 1) {}
}
2. 测试正常运行js耗费时间
js 复制代码
function run1() {
    const data = new Array(2000)
    console.time("normal")
    for(let i = 0; i < data.length; i++) {
        task()
    }
    console.timeEnd("normal")
}

可以看到总耗时2s,页面在js运行期间卡住了,小方块完全没有响应,直到js运行结束后才正常播放动画。

3. 测试使用宏任务方案延迟计算
js 复制代码
function setTimeoutTask() {
    return new Promise((s) => {
        setTimeout(() => {
            task()
            s()
        }, 0)
    }) 
}
function run2() {
    const data = new Array(2000)
    console.time("hong")
    let list = []
    for(let i = 0; i < data.length; i++) {
        list.push(setTimeoutTask())
    }
    Promise.all(list).then(() => {
        console.timeEnd("hong")
    })
}

宏任务方案基本原理是利用setTimeout事件会放到下一个宏任务内运行,从而减少了单个渲染帧的计算压力,但还是会出现间断卡顿的现象。

4. 测试使用空闲帧方案延迟计算
js 复制代码
function idleTask(task) {
    return new Promise((s) => {
        window.requestIdleCallback((IdleDeadline) => {
            let timeRemain = IdleDeadline.timeRemaining();
            if(timeRemain > 0) {
                task()
                s()
            } else {
                idleTask(task).then(() => {
                    s()
                })
            }
        })
    })
}
function run3() {
    const data = new Array(2000)
    console.time("idle")
    let list = []
    for(let i = 0; i < data.length; i++) {
        list.push(idleTask(task))
    }
    Promise.all(list).then(() => {
        console.timeEnd("idle")
    })
}

这里用到requestIdleCallback API,MDN上的说明是"这个函数将在浏览器空闲时期被调用。这使开发者能够在主事件循环上执行后台和低优先级工作,而不会影响延迟关键事件,如动画和输入响应"。个人理解就是在每一帧结束后,如果有空闲时间就会调用。

函数回调内有个值"IdleDeadline.timeRemaining()"用于说明当前帧还剩余多少时间。如果大于0,说明有剩余时间可以用于执行js计算。

实际效果确实是没有卡顿感了,但是相应的执行耗时也比普通执行要长

demo: code.juejin.cn/api/raw/733...

相关推荐
moyu844 分钟前
前端存储三剑客:Cookie、LocalStorage 与 SessionStorage 全方位解析
前端
不爱说话郭德纲8 分钟前
👩‍💼产品姐一句小优化,让我给上百个列表加上一个动态实时计算高度的方法😿😿
前端·vue.js·性能优化
现在没有牛仔了11 分钟前
小试牛刀,用electron+vue3做了一个文件归纳程序~
前端·electron
FogLetter12 分钟前
Prisma + Next.js 全栈开发初体验:像操作对象一样玩转数据库
前端·后端·next.js
知识分享小能手12 分钟前
React学习教程,从入门到精通, React教程:构建你的第一个 React 应用(1)
前端·javascript·vue.js·学习·react.js·ajax·前端框架
李剑一13 分钟前
别乱封装,你看出事儿了吧...
前端·vue.js
文心快码BaiduComate17 分钟前
新增Zulu-CLI、企业版对话支持自定义模型、一键设置自动执行、复用相同终端,8月新能力速览!
前端·后端·程序员
walking95720 分钟前
JavaScript 神技巧!从 “堆代码” 到 “写优雅代码”,前端人必看
前端·面试
前端西瓜哥25 分钟前
图形编辑器开发:基于矩阵的画布缩放和移动实现
前端
walking95725 分钟前
前端 er 收藏!高性价比 JS 工具库,轻量又强大
前端·面试