首页 > 其他分享 >【JS】requestIdleCallback实现分块执行

【JS】requestIdleCallback实现分块执行

时间:2024-10-08 12:21:05浏览次数:9  
标签:requestIdleCallback const 分块 JS length run div document datas

点击按钮后,执行一个耗时较长的dom操作,页面很长时间没有响应,给用户一种卡死的现象

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <button class="btn">添加</button>
  <script>
    const list = Array.from({ length: 100000 }, (_, i) => i)
    const btnElement = document.querySelector(".btn")
    btnElement.onclick = () => {
      for (const ele of list) {
        const div = document.createElement("div")
        div.innerText = ele
        document.body.appendChild(div)
      }
    }
  </script>
</body>

</html>

在这里插入图片描述

可以使用浏览器自带的API:requestIdleCallback,它的设计目的是在浏览器的空闲时间内执行一些低优先级的任务,而不影响主线程的用户交互和渲染。浏览器每次完成一帧渲染之后,如果还有剩余的时间,比如小于16.67 毫秒的时间,就会调用通过 requestIdleCallback 注册的任务。

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <button class="btn">添加</button>
  <script>
    const list = Array.from({ length: 100000 }, (_, i) => i)
    const btnElement = document.querySelector(".btn")
    btnElement.onclick = () => {
      performChunk(list)
    }
    const performChunk = (datas) => {
      if (datas.length === 0) return
      let i = 0
      function _run() {
        if (i >= datas.length) return
        requestIdleCallback((idle) => {
          // 当前空闲时间的剩余毫秒数
          while (idle.timeRemaining() > 0 && i < datas.length) {
            const div = document.createElement("div")
            div.innerText = datas[i]
            document.body.appendChild(div)
            i++
          }
          _run()
        })
      }
      _run()
    }
  </script>
</body>

</html>

在这里插入图片描述

但是 performChunk 通用性还不够强,继续优化,将空闲时间执行逻辑当做参数传递

const list = Array.from({ length: 100000 }, (_, i) => i)
const btnElement = document.querySelector(".btn")
btnElement.onclick = () => {
  const handler = (data, i) => {
    const div = document.createElement("div")
    div.innerText = i
    document.body.appendChild(div)
  }
  performChunk(list, handler)
}
const performChunk = (datas, taskHandler) => {
  if (datas.length === 0) return
  let i = 0
  function _run() {
    if (i >= datas.length) return
    requestIdleCallback((idle) => {
      const remain = idle.timeRemaining() // 当前这一帧剩余的时间
      while (remain > 0 && i < datas.length) {
        taskHandler(datas[i], i)
        i++
      }
      _run()
    })
  }
  _run()
}

继续优化,第一个参数不一定要传数组,也可能为数字,代表执行次数,每一次都执行同样的函数。使用 参数归一化 实现,如果传入数字,将其变为伪数组即可。

const list = Array.from({ length: 100000 }, (_, i) => i)
const btnElement = document.querySelector(".btn")
btnElement.onclick = () => {
  const handler = (data, i) => {
    const div = document.createElement("div")
    div.innerText = i
    document.body.appendChild(div)
  }
  performChunk(100000, handler)
}
const performChunk = (datas, taskHandler) => {
  if (typeof datas === 'number') {
    datas = { length: datas }
  }
  if (datas.length === 0) return
  let i = 0
  function _run() {
    if (i >= datas.length) return
    requestIdleCallback((idle) => {
      const remain = idle.timeRemaining() // 当前这一帧剩余的时间
      while (remain > 0 && i < datas.length) {
        taskHandler(datas[i], i)
        i++
      }
      _run()
    })
  }
  _run()
}

传入第三个函数scheduler调度器,自定义何时进行下一次任务,比如使用计时器

const list = Array.from({ length: 100000 }, (_, i) => i)
const btnElement = document.querySelector(".btn")
btnElement.onclick = () => {
  const taskHandler = (data, i) => {
    const div = document.createElement("div")
    div.innerText = i
    document.body.appendChild(div)
  }
  const scheduler = (task) => {
    setTimeout(() => {
      const start = Date.now()
      task(() => Date.now() - start < 50)
    }, 100);
  }
  performChunk(100000, taskHandler, scheduler)
}
const performChunk = (datas, taskHandler, scheduler) => {
  if (typeof datas === 'number') {
    datas = { length: datas }
  }
  if (datas.length === 0) return
  let i = 0
  function _run() {
    if (i >= datas.length) return
    scheduler((isGoOn) => {
      while (isGoOn() && i < datas.length) {
        taskHandler(datas[i], i)
        i++
      }
      _run()
    })
  }
  _run()
}

继续优化,requestIdleCallback是浏览器API,可以当做默认方式,即 scheduler 为可选参数,默认使用requestIdleCallback

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <button class="btn">添加</button>
  <script>
    const list = Array.from({ length: 100000 }, (_, i) => i)
    const btnElement = document.querySelector(".btn")
    btnElement.onclick = () => {
      const taskHandler = (data, i) => {
        const div = document.createElement("div")
        div.innerText = i
        document.body.appendChild(div)
      }
      browserPerformChunk(100000, taskHandler)
    }
    const performChunk = (datas, taskHandler, scheduler) => {
      if (typeof datas === 'number') {
        datas = { length: datas }
      }
      if (datas.length === 0) return
      let i = 0
      function _run() {
        if (i >= datas.length) return
        scheduler((isGoOn) => {
          while (isGoOn() && i < datas.length) {
            taskHandler(datas[i], i)
            i++
          }
          _run()
        })
      }
      _run()
    }

    function browserPerformChunk(datas, taskHandler) {
      const scheduler = (task) => {
        requestIdleCallback(idle => {
          task(() => idle.timeRemaining() > 0)
        })
      }
      performChunk(datas, taskHandler, scheduler)
    }
  </script>
</body>

</html>

标签:requestIdleCallback,const,分块,JS,length,run,div,document,datas
From: https://blog.csdn.net/owo_ovo/article/details/142659656

相关文章

  • Nuxt.js 应用中的 page:start 钩子详解
    title:Nuxt.js应用中的page:start钩子详解date:2024/10/8updated:2024/10/8author:cmdragonexcerpt:page:start是一个关键的钩子,可以在页面加载时执行必要的逻辑,以提升用户体验。通过合理地使用这个钩子,可以创建流畅的页面导航体验,并提供用户反馈。categories:......
  • DevNow: Search with Lunrjs
    前言假期真快,转眼国庆假期已经到了最后一天。这次国庆没有出去玩,在北京看了看房子,原先的房子快要到期了,找了个更加通透一点的房子,采光也很好。闲暇时间准备优化下DevNow的搜索组件,经过上一版搜索组件优化-Command⌘K的优化,现在的搜索内容只能支持标题,由于有时候标题不能......
  • js学习
    基础语法:1.每一条语句用英文分号结尾(非强制的);2..大小写敏感3.对代码的解释说明,不参与执行单行注释// 多行注释/*多行注释*/4.输出语句所有的输出语句,都不是ES的标准1>document.wirte():用于把数据输出到页面2>alert()把数据有弹窗的方式进行显示......
  • JS刷力扣 【持续跟新】
    1.两数之和【哈希表+数组】前置知识:哈希表:根据键(Key)而直接访问在内存存储位置的数据结构map//常用的几个set()、get()、has()搞明白//1.set(key,value)//2.get(key)返回value//3.has(key)只能判断是否包含某个key,不能判断valueconstmap1=newMap();map1.s......
  • 上传本地pcd文件,并用threejs渲染
    页面<template><div><inputtype="file"@change="handleFileUpload"accept=".pcd"/><inputtype="file"@change="changeFile"/><div@click="stringToFileClick&......
  • three.js 鼠标点击获取模型对象
    three.js 创建模型后,想要实现点击模型获取模型对象触发相应事件。可以使用它提供的APITHREE.Raycaster()https://threejs.org/docs/index.html?q=Raycaster#api/en/core/Raycaster//监听点击事件window.addEventListener('click',(event)=>{event.preventDefault......
  • 网站的时间使用dayjs.js格式化
    1.为什么要使用dayjs在mysql数据库中,datetime字段从数据库读取后,在前台显示出来会带有TZ字母,这是格林尼治时间,如:2024-10-07T12:02:00Z,而我们一般需要2024-10-0712:02:00这种格式。另外,需要在不同地区显示本地时间,那么dayjs会将格林尼时间转换为本地时间。dayjs是一个轻量级的......
  • JS animate() 学习
    animate(keyframes,options)1.关键帧 1/ 如果是过渡动画直接就写效果eg:{transform:'rotate(360deg)'}2/关键帧数组使用数组报过对象eg:[{transform:'translateX(400px)'},{transform:'translateX(300px)'},{transform:'translateX(20px)�......
  • Nuxt.js 应用中的 link:prefetch 钩子详解
    title:Nuxt.js应用中的link:prefetch钩子详解date:2024/10/7updated:2024/10/7author:cmdragonexcerpt:link:prefetch是一个强大的钩子,允许开发者在链接预取时执行附加逻辑。合理利用这个钩子,可以帮助优化页面的加载速度和用户体验,提升Web应用的整体性能。categ......
  • 张量矩阵乘法分块乘法概述
    张量矩阵乘法分块乘法概述介绍一下矩阵计算相关的内容,从最基本的算法,到Cutlass这些线性代数模版库,特别是Layout代数相关的内容,再逐渐细化到一些硬件实现访存优化和一些算子融合。6.3.1GEMM概述1.GEMM定义对于一个矩阵乘法,定义如下: (6-1)一个矩阵乘法定义,如图6-26......