点击按钮后,执行一个耗时较长的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