当我们遇到任务密集型操作时我们需要考虑浏览器的性能压力,将某一段密集型操作分成几段任务,并且在浏览器空闲时进行操作,这将大大减少性能消耗
当我们面试时被问到如果后端一次性给你返回10万条数据,前端该怎么去处理,并将数据正常显示,当然对于我们前端来说我们可能直接内心翻腾,但是这种时候就是需要结合场景来解决问题,给钱的是大爷....
我们可以根据这个问题进行拆分:
- 在浏览器中怎么去进行分段
- 怎么知道浏览器空闲了,并且在浏览器下一次空闲时再执行任务
- 怎么保证浏览器渲染正常
带着问题我们可以去调研,发现有个api叫做requestIdleCallback,他能在浏览器每一帧渲染的空闲时执行回调,所以之歌api完美适用我们的分段需求:
const tasks = Array.from({ length: 500000 }, (_, i) => () => {
const div = document.createElement("div");
div.innerText = i;
document.body.appendChild(div);
});
上面的代码是创建一个50万的数组,每一个元素都是一个函数,执行的操作是创建一个div插入到body中,这毫无疑问是一个密集型操作,所以我们对该操作进行分段
function idlePerformTask(tasks) {
//先思考怎么去分时,通过
performTask(tasks, (_runTask) => {
//每一帧渲染后有空闲执行
requestIdleCallback((IdleDeadline) => {
// IdleDeadline.timeRemaining() > 0表示还有空闲
_runTask(() => IdleDeadline.timeRemaining() > 0);
});
});
}
然后我们再去进行具体操作的函数抽离
function performTask(tasks, scheduler) {
//记录下标拿到对应需要执行的函数
let index = 0;
const _run = () => {
//调度器开始执行,此时的isGo就是() => IdleDeadline.timeRemaining() > 0这个函数
scheduler((isGo) => {
console.log(11111);
//判断下标是否取完,然后浏览器是否空闲
while (index < tasks.length && isGo()) {
//执行插入元素操作
tasks[index]();
//下标自增
index++;
}
//递归执行调用
_run();
});
};
//第一次执行一下
_run();
}
优化考虑防止_run空转递归加一下判断条件
scheduler((isGo) => {
console.log(11111);
while (index < tasks.length && isGo()) {
tasks[index]();
index++;
}
if (index < tasks.length) {
_run(); // 还有任务才继续调度
}
});