如何在浏览器中执行 100 万个任务,并保证页面不卡顿?

最后更新:1/13/2026

AI 助手

会员专享功能

只有会员才能使用 AI 生成答案功能。 立即升级

Other Answers (1)

如何在浏览器中执行 100 万个任务并保证页面不卡顿?

核心问题分析

浏览器执行大量任务时卡顿的主要原因是:

  1. 主线程阻塞:JavaScript 是单线程的,长时间运行会阻塞 UI 渲染
  2. 帧率下降:每帧执行时间超过 16.67ms(60fps)会导致视觉卡顿
  3. 内存压力:大量数据处理可能导致内存泄漏或 GC 停顿

解决方案策略

1. 任务分片(Task Slicing)

将大任务拆分为多个小任务,在空闲时间执行

javascript
// 使用 setTimeout/setInterval 分片
function processTasksWithTimeout(tasks, chunkSize = 1000) {
    let index = 0;
    
    function processChunk() {
        const end = Math.min(index + chunkSize, tasks.length);
        
        for (; index < end; index++) {
            // 执行任务
            tasks[index]();
        }
        
        if (index < tasks.length) {
            // 让出主线程控制权
            setTimeout(processChunk, 0);
        }
    }
    
    processChunk();
}

2. 使用 requestIdleCallback

在浏览器空闲时段执行低优先级任务

javascript
function processTasksWithIdleCallback(tasks) {
    let index = 0;
    
    function processTask(deadline) {
        // 在空闲时间内尽可能多地执行任务
        while (deadline.timeRemaining() > 0 && index < tasks.length) {
            tasks[index]();
            index++;
        }
        
        if (index < tasks.length) {
            // 继续调度下一个空闲时段
            requestIdleCallback(processTask);
        }
    }
    
    requestIdleCallback(processTask);
}

3. 使用 requestAnimationFrame

将任务执行与浏览器渲染周期对齐

javascript
function processTasksWithAnimationFrame(tasks, chunkSize = 100) {
    let index = 0;
    
    function processChunk() {
        const startTime = performance.now();
        
        // 每帧最多执行 chunkSize 个任务或执行到 10ms
        while (index < tasks.length && 
               performance.now() - startTime < 10) {
            if (index % chunkSize === 0 && index !== 0) {
                // 每 chunkSize 个任务检查一次时间
                if (performance.now() - startTime >= 10) {
                    break;
                }
            }
            tasks[index]();
            index++;
        }
        
        if (index < tasks.length) {
            requestAnimationFrame(processChunk);
        }
    }
    
    requestAnimationFrame(processChunk);
}

4. Web Workers 并行处理

将计算密集型任务移到后台线程

javascript
// main.js
const worker = new Worker('task-worker.js');
const tasks = Array.from({length: 1000000}, (_, i) => i);

// 发送任务数据
worker.postMessage({ tasks });

// 接收处理结果
worker.onmessage = function(event) {
    console.log('处理结果:', event.data);
};

// task-worker.js
self.onmessage = function(event) {
    const tasks = event.data.tasks;
    const results = [];
    
    // 在 Worker 中处理,不会阻塞主线程
    for (let i = 0; i < tasks.length; i++) {
        results.push(processTask(tasks[i]));
    }
    
    self.postMessage(results);
};

function processTask(task) {
    // 任务处理逻辑
    return task * 2;
}

5. 增量处理与流式处理

javascript
class TaskScheduler {
    constructor(tasks, options = {}) {
        this.tasks = tasks;
        this.index = 0;
        this.chunkSize = options.chunkSize || 100;
        this.timeBudget = options.timeBudget || 10; // ms
        this.priority = options.priority || 'normal';
    }
    
    start() {
        if (this.priority === 'high') {
            this.processWithAnimationFrame();
        } else if (this.priority === 'low') {
            this.processWithIdleCallback();
        } else {
            this.processWithTimeout();
        }
    }
    
    processWithAnimationFrame() {
        const processChunk = () => {
            const startTime = performance.now();
            let processed = 0;
            
            while (this.index < this.tasks.length && 
                   processed < this.chunkSize &&
                   performance.now() - startTime < this.timeBudget) {
                this.executeTask(this.tasks[this.index]);
                this.index++;
                processed++;
            }
            
            if (this.index < this.tasks.length) {
                requestAnimationFrame(processChunk);
            } else {
                this.onComplete();
            }
        };
        
        requestAnimationFrame(processChunk);
    }
    
    executeTask(task) {
        // 执行单个任务
        try {
            task();
        } catch (error) {
            console.error('任务执行失败:', error);
        }
    }
    
    onComplete() {
        console.log('所有任务处理完成');
    }
}

6. 虚拟化与懒加载

对于 DOM 操作任务,只处理可见区域

javascript
class VirtualTaskProcessor {
    constructor(container, totalTasks, renderTask) {
        this.container = container;
        this.totalTasks = totalTasks;
        this.renderTask = renderTask;
        this.visibleCount = 50;
        this.buffer = 10;
        
        this.init();
    }
    
    init() {
        this.container.addEventListener('scroll', this.handleScroll.bind(this));
        this.renderVisibleTasks();
    }
    
    handleScroll() {
        requestAnimationFrame(() => {
            this.renderVisibleTasks();
        });
    }
    
    renderVisibleTasks() {
        const scrollTop = this.container.scrollTop;
        const startIndex = Math.floor(scrollTop / 50);
        const endIndex = Math.min(startIndex + this.visibleCount + this.buffer, this.totalTasks);
        
        // 只渲染可见区域及缓冲区的任务
        for (let i = startIndex; i < endIndex; i++) {
            this.renderTask(i);
        }
    }
}

最佳实践组合方案

javascript
async function executeMillionTasksOptimized(tasks) {
    // 1. 分类任务
    const uiTasks = tasks.filter(t => t.type === 'ui');
    const computeTasks = tasks.filter(t => t.type === 'compute');
    const ioTasks = tasks.filter(t => t.type === 'io');
    
    // 2. 使用 Web Worker 处理计算密集型任务
    if (computeTasks.length > 10000) {
        await processWithWorker(computeTasks);
    } else {
        await processWithScheduler(computeTasks, { priority: 'low' });
    }
    
    // 3. 使用 requestAnimationFrame 处理 UI 任务
    await processWithScheduler(uiTasks, { priority: 'high', timeBudget: 8 });
    
    // 4. 使用 idle callback 处理低优先级任务
    await processWithScheduler(ioTasks, { priority: 'low' });
}

// 进度反馈
function createProgressReporter(total) {
    let processed = 0;
    
    return {
        update() {
            processed++;
            const percent = (processed / total * 100).toFixed(1);
            
            // 使用 requestAnimationFrame 更新进度,避免频繁重绘
            requestAnimationFrame(() => {
                document.getElementById('progress').textContent = `${percent}%`;
            });
        },
        getProgress() {
            return processed / total;
        }
    };
}

性能监控与调优

javascript
class PerformanceMonitor {
    constructor() {
        this.metrics = {
            fps: [],
            memory: [],
            taskDuration: []
        };
    }
    
    startMonitoring() {
        // 监控 FPS
        this.monitorFPS();
        
        // 监控内存
        if (performance.memory) {
            this.monitorMemory();
        }
        
        // 监控长任务
        if ('PerformanceObserver' in window) {
            this.observeLongTasks();
        }
    }
    
    monitorFPS() {
        let frameCount = 0;
        let lastTime = performance.now();
        
        const checkFPS = () => {
            frameCount++;
            const currentTime = performance.now();
            
            if (currentTime - lastTime >= 1000) {
                const fps = Math.round((frameCount * 1000) / (currentTime - lastTime));
                this.metrics.fps.push(fps);
                
                if (fps < 50) {
                    console.warn(`低帧率警告: ${fps}fps`);
                }
                
                frameCount = 0;
                lastTime = currentTime;
            }
            
            requestAnimationFrame(checkFPS);
        };
        
        requestAnimationFrame(checkFPS);
    }
    
    observeLongTasks() {
        const observer = new PerformanceObserver((list) => {
            for (const entry of list.getEntries()) {
                if (entry.duration > 50) {
                    console.warn(`长任务检测: ${entry.duration}ms`);
                    this.metrics.taskDuration.push(entry.duration);
                }
            }
        });
        
        observer.observe({ entryTypes: ['longtask'] });
    }
}

关键要点总结

  1. 分而治之:将大任务拆分为小任务分片执行
  2. 优先级调度:根据任务类型选择合适的调度策略
    • UI 更新:使用 requestAnimationFrame
    • 计算任务:使用 Web Workers
    • 低优先级任务:使用 requestIdleCallback
  3. 避免同步阻塞:不要在主线程执行长时间同步操作
  4. 内存管理:及时清理不再使用的数据,避免内存泄漏
  5. 进度反馈:给用户提供进度提示,改善体验
  6. 性能监控:实时监控性能指标,动态调整策略

通过组合使用这些技术,可以在浏览器中高效处理百万级任务,同时保持页面的流畅响应。