分时函数
大约 2 分钟
分时函数
当有大量的渲染任务或者其他同步任务需要执行时,如果一次性全部执行可能会大量占用浏览器资源,导致浏览器无法及时执行从而卡顿,这样就会对使用体验造成不好的影响,此时我们就应当合理的安排这些任务的执行时机以缓解浏览器的压力。
具体实现
分时函数可以将任务进行分割,将分割后的小段任务分配在浏览器的执行空闲时间段来执行,从而显著降低了浏览器的资源占用,它的实现如下:
// 分时函数可以将大量任务分割,接收 3 个参数:任务列表,执行任务的回调,分割执行任务的回调
function splitTask(list, callback, splitor) {
if (!list.length) {
return
}
//如果没有传入 splitor 参数(分割任务执行器),就使用浏览器提供的 requestIdleCallback
if ((!splitor || typeof splitor !== 'function') && typeof window.requestIdleCallback === 'function') {
splitor = window.requestIdleCallback
}
//设置当前任务索引,初始值为 0
let index = 0
//保存任务列表的长度
const len = list.length
//判断当前是否应当执行任务,接收一个自定义的执行时间判断回调和当前时间参数
const shouldRun = (timeCallback, now) => {
//执行传入的自定义执行时间回调,传入当前时间与开始执行时间的差值,判断是否小于 16.6(浏览器刷新一帧的时间)
if (timeCallback && typeof timeCallback === 'function') {
return timeCallback(Date.now() - now < 16.6)
}
// requestIdleCallback 特有的回调参数 didTimeout来判断是否应当执行
else if (timeCallback && typeof timeCallback === 'object' && timeCallback.didTimeout !== null) {
return !timeCallback.didTimeout
}
return Date.now() - now < 16.6
}
//具体执行任务的函数
function runTask() {
if (index === list.length) {
return
}
//通过任务执行分割器来执行任务
splitor((remaining) => {
//获取当前时间
let now = Date.now()
//while 循环执行任务
while (shouldRun(remaining, now) && index < len) {
const task = list[index]
callback && callback(task, index)
//更新当前时间
now = Date.now()
index++
}
//递归调用不间断执行
runTask()
})
}
runTask()
}
//使用
const tasks = new Array(10000).fill(0).map(() => {
return {
task: (param) => console.log(param)
}
})
const run = (task, index) => {
console.log(`第 ${index + 1} 个任务`)
}
splitTask(tasks, run)
// splitTask(tasks, run, (task) => {
// setTimeout(() => {
// task((time) => time < 16)
// }, 200);
// })