如果需要使用 JS 执行 100 万个任务,如何保证浏览器不卡顿?
1. 使用分块处理(Chunking)
将 100 万个任务分成小块,逐块处理,每块处理完成后将控制权交还给浏览器,利用空闲时间继续处理。
实现方式:setTimeout 或 setInterval
javascript
function processInChunks(tasks, chunkSize = 100) {
function processChunk() {
const chunk = tasks.splice(0, chunkSize);
chunk.forEach(task => task());
if (tasks.length > 0) {
setTimeout(processChunk, 0); // 让出主线程
}
}
processChunk();
}
// 示例
const tasks = Array.from({ length: 1000000 }, (_, i) => () => console.log(i));
processInChunks(tasks);2. 使用浏览器空闲时间:requestIdleCallback
requestIdleCallback 可以利用浏览器的空闲时间执行任务,避免占用主线程的关键资源时间。
javascript
function processWithIdleCallback(tasks) {
function processChunk(deadline) {
while (deadline.timeRemaining() > 0 && tasks.length > 0) {
const task = tasks.shift();
task();
}
if (tasks.length > 0) {
requestIdleCallback(processChunk);
}
}
requestIdleCallback(processChunk);
}
// 示例
const tasks = Array.from({ length: 1000000 }, (_, i) => () => console.log(i));
processWithIdleCallback(tasks);3. 使用 Web Workers
将任务放到 Web Worker 中执行,避免阻塞主线程。
主线程代码:
javascript
const worker = new Worker("worker.js");
worker.postMessage(1000000); // 发送任务数量
worker.onmessage = (e) => {
console.log(e.data); // 接收 worker 处理结果
};Worker 脚本(worker.js):
javascript
onmessage = (e) => {
const tasks = e.data;
for (let i = 0; i < tasks; i++) {
// 模拟任务
}
postMessage("All tasks completed!");
};4. 批处理和任务调度器
创建自定义任务调度器,根据优先级和剩余时间动态分配任务。
javascript
class Scheduler {
constructor() {
this.tasks = [];
}
add(task) {
this.tasks.push(task);
}
run(chunkSize = 100) {
const execute = () => {
const chunk = this.tasks.splice(0, chunkSize);
chunk.forEach(task => task());
if (this.tasks.length > 0) {
setTimeout(execute, 0);
}
};
execute();
}
}
// 示例
const scheduler = new Scheduler();
for (let i = 0; i < 1000000; i++) {
scheduler.add(() => console.log(i));
}
scheduler.run();5. 使用 async/await 与微任务
利用 Promise 和 await 将任务切分到微任务队列中,减少对主线程的持续占用。
javascript
async function processTasks(tasks) {
for (let i = 0; i < tasks.length; i++) {
tasks[i]();
if (i % 100 === 0) {
await new Promise(resolve => setTimeout(resolve, 0)); // 让出主线程
}
}
}
// 示例
const tasks = Array.from({ length: 1000000 }, (_, i) => () => console.log(i));
processTasks(tasks);题目要点:
为了保证浏览器不卡顿,核心思路是避免长时间占用主线程,让浏览器有机会执行渲染和其他任务。
具体的方法有:
- 使用 分块处理 或 空闲时间调度。
- 利用 Web Workers 将任务移到子线程。
- 结合 Promise 和 异步操作 处理任务。