关键词:大批量执行任务不卡顿
Web Workers
要确保浏览器在执行100万个任务时不会卡顿,你可以考虑使用Web Workers来将这些任务从主线程中分离出来。Web Workers允许在后台线程中运行脚本,从而避免阻塞主线程,保持页面的响应性。
以下是一个使用Web Workers的简单示例:
// 主线程代码
const worker = new Worker('worker.js'); // 创建一个新的Web Worker
worker.postMessage({ start: 0, end: 1000000 }); // 向Web Worker发送消息
worker.onmessage = function(event) {
const result = event.data;
console.log('任务完成:', result);
};
// worker.js - Web Worker代码
onmessage = function(event) {
const start = event.data.start;
const end = event.data.end;
let sum = 0;
for (let i = start; i <= end; i++) {
sum += i;
}
postMessage(sum); // 向主线程发送消息
};
在这个示例中,主线程创建了一个新的Web Worker,并向其发送了一个包含任务范围的消息。Web Worker在后台线程中执行任务,并将结果发送回主线程。
requestAnimationFrame 来实现任务分割
使用requestAnimationFrame
来实现任务分割是一种常见的方式,它可以确保任务在浏览器的每一帧之间执行,从而避免卡顿。以下是一个使用requestAnimationFrame
来分割任务的简单例子:
// 假设有一个包含大量元素的数组
const bigArray = Array.from({ length: 1000000 }, (_, i) => i + 1);
// 定义一个处理函数,例如对数组中的每个元素进行平方操作
function processChunk(chunk) {
return chunk.map(num => num * num);
}
// 分割任务并使用requestAnimationFrame
const chunkSize = 1000; // 每个小块的大小
let index = 0;
function processArrayWithRAF() {
function processChunkWithRAF() {
const chunk = bigArray.slice(index, index + chunkSize); // 从大数组中取出一个小块
const result = processChunk(chunk); // 处理小块任务
console.log('处理完成:', result);
index += chunkSize;
if (index < bigArray.length) {
requestAnimationFrame(processChunkWithRAF); // 继续处理下一个小块
}
}
requestAnimationFrame(processChunkWithRAF); // 开始处理大数组
}
processArrayWithRAF();
在这个例子中,我们使用requestAnimationFrame
来循环执行处理小块任务的函数processChunkWithRAF
,从而实现对大数组的任务分割。这样可以确保任务在每一帧之间执行,避免卡顿。
针对上面的改进一下
const chunkSize = 1000; // 每个小块的大小
是不能保证不卡的, 那么久需要动态调整 chunkSize
的大小, 代码可以参考下面的示范:
const $result = document.getElementById("result");
// 假设有一个包含大量元素的数组
const bigArray = Array.from({ length: 1000000 }, (_, i) => i + 1);
// 定义一个处理函数,对数组中的每个元素执行一次
function processChunk(chunk) {
return `chunk: ${chunk}`;
}
// 动态调整 chunkSize 的优化方式
let chunkSize = 1000; // 初始的 chunkSize
let index = 0;
function processArrayWithDynamicChunkSize() {
function processChunkWithRAF() {
let startTime = performance.now(); // 记录结束时间
for (let i = 0; i < chunkSize; i++) {
if (index < bigArray.length) {
const result = processChunk(bigArray[index]); // 对每个元素执行处理函数
$result.innerText = result;
index++;
}
}
let endTime = performance.now();
let timeTaken = endTime - startTime; // 计算处理时间
// 根据处理时间动态调整 chunkSize
if (timeTaken > 16) { // 如果处理时间超过一帧的时间(16毫秒),则减小 chunkSize
chunkSize = Math.floor(chunkSize * 0.9); // 减小10%
} else if (timeTaken < 16) { // 如果处理时间远小于一帧的时间(8毫秒),则增加 chunkSize
chunkSize = Math.floor(chunkSize * 1.1); // 增加10%
}
if (index < bigArray.length) {
requestAnimationFrame(processChunkWithRAF); // 继续处理下一个小块
}
}
requestAnimationFrame(processChunkWithRAF); // 开始处理大数组
}
processArrayWithDynamicChunkSize();
在这个例子中,我们动态调整chunkSize
的大小,根据处理时间来优化任务分割。根据处理时间的表现,动态调整chunkSize
的大小,以确保在处理大量任务时,浏览器能够保持流畅,避免卡顿。
参考文档: 100万个函数执行保证浏览器不卡
requestIdleCallback
window.requestIdleCallback
是一个用于在浏览器空闲时执行任务的API。它允许开发者在浏览器的主线程空闲时执行一些任务,而不会影响用户界面的流畅性和响应性。
这个 API 的基本思想是利用浏览器在空闲时的空闲时间来执行任务,这样就可以避免在用户执行交互操作时造成卡顿。requestIdleCallback
接受一个回调函数作为参数,该回调函数会在浏览器空闲时被调用。
以下是 window.requestIdleCallback
的基本用法:
window.requestIdleCallback(function(deadline) {
// 在空闲时执行的任务
// deadline 参数提供了一些信息,比如剩余的空闲时间等
});
requestIdleCallback
的回调函数接收一个 deadline
参数,它包含了一些有关当前空闲时间的信息。通过这个参数,你可以决定是否继续执行任务或者推迟到下一次空闲时段。
此外,还有一个配套的 window.cancelIdleCallback
方法,用于取消通过 requestIdleCallback
请求的回调:
const id = window.requestIdleCallback(function(deadline) {
// 在空闲时执行的任务
});
// 取消回调
window.cancelIdleCallback(id);
需要注意的是,requestIdleCallback
并不是所有浏览器都支持的标准,因此在使用时要注意检查浏览器的兼容性。在一些现代浏览器中,这个 API 已经得到了广泛的支持,但在某些老旧的浏览器中可能并不可用。