Skip to content

webworker优势演示 #20

@leno23

Description

@leno23
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Web Worker 优势演示</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            padding: 20px;
            display: flex;
            justify-content: center;
            align-items: center;
        }

        .container {
            max-width: 900px;
            width: 100%;
            background: white;
            border-radius: 20px;
            box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
            padding: 40px;
        }

        h1 {
            color: #333;
            text-align: center;
            margin-bottom: 10px;
            font-size: 32px;
        }

        .subtitle {
            text-align: center;
            color: #666;
            margin-bottom: 40px;
            font-size: 16px;
        }

        .demo-section {
            margin-bottom: 30px;
            padding: 25px;
            background: #f8f9fa;
            border-radius: 12px;
            border: 2px solid #e9ecef;
        }

        .demo-section h2 {
            color: #495057;
            margin-bottom: 15px;
            font-size: 20px;
            display: flex;
            align-items: center;
        }

        .demo-section h2::before {
            content: '';
            display: inline-block;
            width: 4px;
            height: 20px;
            background: #667eea;
            margin-right: 10px;
            border-radius: 2px;
        }

        .demo-section p {
            color: #6c757d;
            margin-bottom: 20px;
            line-height: 1.6;
        }

        .button-group {
            display: flex;
            gap: 15px;
            flex-wrap: wrap;
        }

        button {
            flex: 1;
            min-width: 150px;
            padding: 12px 24px;
            font-size: 16px;
            font-weight: 600;
            border: none;
            border-radius: 8px;
            cursor: pointer;
            transition: all 0.3s ease;
            color: white;
        }

        .btn-block {
            background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
        }

        .btn-block:hover {
            transform: translateY(-2px);
            box-shadow: 0 5px 15px rgba(245, 87, 108, 0.4);
        }

        .btn-worker {
            background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
        }

        .btn-worker:hover {
            transform: translateY(-2px);
            box-shadow: 0 5px 15px rgba(79, 172, 254, 0.4);
        }

        button:disabled {
            opacity: 0.6;
            cursor: not-allowed;
            transform: none !important;
        }

        .status {
            margin-top: 15px;
            padding: 15px;
            background: white;
            border-radius: 8px;
            min-height: 80px;
            border-left: 4px solid #667eea;
        }

        .status-title {
            font-weight: 600;
            color: #495057;
            margin-bottom: 8px;
        }

        .status-content {
            color: #6c757d;
            font-size: 14px;
            line-height: 1.6;
        }

        .highlight {
            color: #667eea;
            font-weight: 600;
        }

        .success {
            color: #28a745;
            font-weight: 600;
        }

        .animation-test {
            margin-top: 20px;
            padding: 20px;
            background: white;
            border-radius: 8px;
            text-align: center;
        }

        .animation-test h3 {
            color: #495057;
            margin-bottom: 15px;
            font-size: 16px;
        }

        .spinner {
            width: 50px;
            height: 50px;
            margin: 0 auto;
            border: 4px solid #f3f3f3;
            border-top: 4px solid #667eea;
            border-radius: 50%;
            /* 不使用CSS动画,改用JavaScript驱动 */
        }

        .fps-counter {
            margin-top: 10px;
            font-size: 18px;
            font-weight: bold;
            color: #667eea;
        }

        .frame-indicator {
            display: inline-block;
            width: 20px;
            height: 20px;
            background: #28a745;
            border-radius: 50%;
            margin-left: 10px;
            animation: pulse 1s ease-in-out infinite;
        }

        @keyframes pulse {
            0%, 100% { opacity: 1; transform: scale(1); }
            50% { opacity: 0.5; transform: scale(0.8); }
        }

        .info-box {
            margin-top: 30px;
            padding: 20px;
            background: #e7f3ff;
            border-radius: 12px;
            border-left: 4px solid #0066cc;
        }

        .info-box h3 {
            color: #0066cc;
            margin-bottom: 10px;
            font-size: 18px;
        }

        .info-box ul {
            list-style: none;
            padding-left: 0;
        }

        .info-box li {
            color: #495057;
            margin-bottom: 8px;
            padding-left: 24px;
            position: relative;
            line-height: 1.6;
        }

        .info-box li::before {
            content: '✓';
            position: absolute;
            left: 0;
            color: #28a745;
            font-weight: bold;
        }

        .progress-bar {
            width: 100%;
            height: 8px;
            background: #e9ecef;
            border-radius: 4px;
            overflow: hidden;
            margin-top: 10px;
        }

        .progress-fill {
            height: 100%;
            background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
            width: 0%;
            transition: width 0.3s ease;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>🚀 Web Worker 优势演示</h1>
        <p class="subtitle">对比主线程计算 vs Web Worker 后台计算</p>

        <!-- 主线程计算 -->
        <div class="demo-section">
            <h2>❌ 主线程计算(造成卡顿)</h2>
            <p>点击按钮后,将执行1亿次密集计算,分成50批在主线程执行。这会<strong>造成严重卡顿</strong>,动画变得断断续续,FPS大幅下降,页面响应缓慢。</p>
            <div class="button-group">
                <button class="btn-block" onclick="calculateInMainThread()">
                    开始主线程计算
                </button>
            </div>
            <div class="status" id="mainThreadStatus">
                <div class="status-title">状态:</div>
                <div class="status-content">等待开始...</div>
            </div>
        </div>

        <!-- Web Worker 计算 -->
        <div class="demo-section">
            <h2>✅ Web Worker 计算(流畅不卡)</h2>
            <p>点击按钮后,执行完全相同的1亿次计算。计算会在Web Worker后台线程中执行,<strong>完全不卡顿</strong>,动画持续流畅,FPS保持60,还能实时更新进度。</p>
            <div class="button-group">
                <button class="btn-worker" onclick="calculateInWorker()">
                    开始Worker计算
                </button>
            </div>
            <div class="status" id="workerStatus">
                <div class="status-title">状态:</div>
                <div class="status-content">等待开始...</div>
            </div>
        </div>

        <!-- 动画测试区域 -->
        <div class="animation-test">
            <h3>🎯 UI响应性测试 - JavaScript动画(依赖主线程)</h3>
            <div class="spinner" id="spinner"></div>
            <div class="fps-counter">
                FPS: <span id="fpsValue">60</span>
                <span class="frame-indicator" id="frameIndicator"></span>
            </div>
            <p style="margin-top: 15px; color: #6c757d; font-size: 14px;">
                主线程计算时:动画变得<strong>卡顿</strong>,FPS降到<strong>10-20</strong>,转动不流畅<br>
                Worker计算时:动画<strong>完全流畅</strong>,FPS保持<strong>60</strong>,丝般顺滑
            </p>
        </div>

        <!-- 技术说明 -->
        <div class="info-box" style="background: #fff3cd; border-left-color: #ffc107;">
            <h3 style="color: #856404;">⚙️ 技术实现说明</h3>
            <ul>
                <li><strong>JavaScript动画:</strong>使用 requestAnimationFrame 驱动动画,完全依赖主线程事件循环</li>
                <li><strong>分批计算:</strong>主线程将计算分成50批,每批约100ms,通过setTimeout串联,造成持续卡顿</li>
                <li><strong>卡顿原理:</strong>每批计算占用主线程时,RAF执行被延迟,动画更新不及时,FPS下降</li>
                <li><strong>Worker优势:</strong>计算在后台线程,主线程完全空闲,动画帧正常执行,FPS保持60</li>
                <li><strong>真实场景:</strong>这种卡顿更接近实际应用(如大数据处理、图像处理等)的体验</li>
            </ul>
        </div>

        <!-- 对比说明 -->
        <div class="info-box" style="background: #f8d7da; border-left-color: #f5576c;">
            <h3 style="color: #721c24;">📊 效果对比</h3>
            <ul style="list-style: none; padding-left: 0;">
                <li style="margin-bottom: 12px;">
                    <strong style="color: #f5576c;">❌ 主线程计算:</strong><br>
                    <span style="padding-left: 24px; display: block;">动画卡顿、掉帧严重、FPS降到10-20、用户体验差、页面响应慢</span>
                </li>
                <li>
                    <strong style="color: #28a745;">✅ Worker计算:</strong><br>
                    <span style="padding-left: 24px; display: block;">动画流畅、不掉帧、FPS稳定60、用户体验好、页面完全响应</span>
                </li>
            </ul>
        </div>

        <!-- 信息说明 -->
        <div class="info-box">
            <h3>💡 Web Worker 的优势</h3>
            <ul>
                <li><strong>不阻塞UI:</strong>在后台线程执行代码,保持页面响应性</li>
                <li><strong>更好的性能:</strong>充分利用多核CPU,提升应用性能</li>
                <li><strong>改善用户体验:</strong>避免页面卡顿,动画和交互保持流畅</li>
                <li><strong>适用场景:</strong>大数据处理、复杂计算、图像处理、加密解密、视频编码等</li>
            </ul>
        </div>
    </div>

    <script>
        let worker = null;
        let animationId = null;
        let rotation = 0;
        let lastFrameTime = performance.now();
        let frameCount = 0;
        let fps = 60;

        // 使用 requestAnimationFrame 驱动的 JavaScript 动画
        function animateSpinner() {
            const spinner = document.getElementById('spinner');
            const fpsValue = document.getElementById('fpsValue');
            const now = performance.now();
            
            // 更新旋转角度
            rotation += 3; // 每帧旋转3度
            if (rotation >= 360) rotation -= 360;
            spinner.style.transform = `rotate(${rotation}deg)`;
            
            // 计算FPS(每秒更新一次)
            frameCount++;
            if (now - lastFrameTime >= 1000) {
                fps = Math.round((frameCount * 1000) / (now - lastFrameTime));
                fpsValue.textContent = fps;
                frameCount = 0;
                lastFrameTime = now;
            }
            
            // 继续下一帧动画
            animationId = requestAnimationFrame(animateSpinner);
        }

        // 启动动画
        function startAnimation() {
            if (!animationId) {
                animateSpinner();
            }
        }

        // 停止动画(仅用于调试,实际不需要)
        function stopAnimation() {
            if (animationId) {
                cancelAnimationFrame(animationId);
                animationId = null;
            }
        }

        // 创建内联的 Web Worker
        function createInlineWorker() {
            const workerCode = `
                // Web Worker 代码
                // 这个代码会在独立的后台线程中运行,不会阻塞主线程
                
                // 监听主线程发来的消息
                self.onmessage = function (e) {
                    const { totalIterations, batchSize } = e.data;
                    const startTime = performance.now();
                    let iterations = 0;
                    let sum = 0;
                    
                    // 分批执行计算
                    function processBatch() {
                        const batchEnd = Math.min(iterations + batchSize, totalIterations);
                        
                        // 执行一批计算
                        for (let i = iterations; i < batchEnd; i++) {
                            sum += Math.sqrt(i) + Math.sin(i) + Math.cos(i);
                            if (i % 1000000 === 0) {
                                sum = sum % 1000000;
                            }
                        }
                        
                        iterations = batchEnd;
                        const progress = Math.floor((iterations / totalIterations) * 100);
                        
                        // 报告进度
                        self.postMessage({
                            type: "progress",
                            progress: progress,
                            iterations: iterations,
                            elapsed: (performance.now() - startTime).toFixed(0),
                        });
                        
                        // 检查是否完成
                        if (iterations < totalIterations) {
                            // 继续下一批,使用setTimeout让Worker可以处理其他任务
                            setTimeout(processBatch, 0);
                        } else {
                            // 计算完成
                            const endTime = performance.now();
                            const duration = (endTime - startTime).toFixed(2);
                            
                            self.postMessage({
                                type: "complete",
                                iterations: iterations,
                                sum: sum,
                                time: duration,
                            });
                        }
                    }
                    
                    // 开始处理
                    processBatch();
                };
                
                // 错误处理
                self.onerror = function (error) {
                    console.error("Worker 内部错误:", error);
                    self.postMessage({
                        type: "error",
                        message: error.message,
                    });
                };
            `;
            
            // 创建 Blob 对象
            const blob = new Blob([workerCode], { type: 'application/javascript' });
            // 创建 Worker URL
            const workerUrl = URL.createObjectURL(blob);
            // 创建 Worker 实例
            return new Worker(workerUrl);
        }

        // 初始化 Web Worker
        function initWorker() {
            if (!worker) {
                worker = createInlineWorker();
                worker.onmessage = function(e) {
                    const statusDiv = document.getElementById('workerStatus');
                    if (e.data.type === 'progress') {
                        statusDiv.innerHTML = `
                            <div class="status-title">状态:计算中...</div>
                            <div class="status-content">
                                进度: <span class="highlight">${e.data.progress}%</span><br>
                                已执行: <span class="highlight">${e.data.iterations.toLocaleString()}</span> 次迭代<br>
                                已耗时: <span class="highlight">${e.data.elapsed}ms</span><br>
                                <span style="color: #28a745;">✓ UI完全流畅,FPS保持60,动画不卡顿</span>
                            </div>
                            <div class="progress-bar">
                                <div class="progress-fill" style="width: ${e.data.progress}%"></div>
                            </div>
                        `;
                    } else if (e.data.type === 'complete') {
                        statusDiv.innerHTML = `
                            <div class="status-title">状态:完成 ✓</div>
                            <div class="status-content">
                                <span class="success">计算完成!</span><br>
                                执行迭代: <span class="highlight">${e.data.iterations.toLocaleString()}</span> 次<br>
                                计算结果: <span class="highlight">${e.data.sum.toFixed(2)}</span><br>
                                总耗时: <span class="highlight">${e.data.time}ms</span> (约${(e.data.time/1000).toFixed(1)}秒)<br>
                                <span style="color: #28a745;">✓ 整个过程UI始终流畅,FPS保持60,无任何卡顿</span>
                            </div>
                        `;
                    }
                };

                worker.onerror = function(error) {
                    console.error('Worker error:', error);
                    document.getElementById('workerStatus').innerHTML = `
                        <div class="status-title">错误:</div>
                        <div class="status-content" style="color: #dc3545;">
                            ${error.message}
                        </div>
                    `;
                };
            }
        }

        // 主线程计算(会造成卡顿)
        function calculateInMainThread() {
            const statusDiv = document.getElementById('mainThreadStatus');
            
            statusDiv.innerHTML = `
                <div class="status-title">状态:计算中...</div>
                <div class="status-content">
                    <span style="color: #f5576c;">⚠️ 主线程计算中,观察FPS下降和动画卡顿</span><br>
                    进度: <span id="mainProgress">0</span>% | 批次: <span id="mainBatch">0</span>/50
                </div>
                <div class="progress-bar">
                    <div class="progress-fill" id="mainProgressBar" style="width: 0%"></div>
                </div>
            `;
            
            const startTime = performance.now();
            let iterations = 0;
            let sum = 0;
            let currentStep = 0;
            const totalSteps = 50; // 分成50个批次
            const iterationsPerStep = 2000000; // 每批次200万次迭代
            
            // 使用分批计算模拟卡顿效果
            function doCalculationBatch() {
                // 执行一批密集计算(阻塞约100ms)
                const batchStart = performance.now();
                for (let i = 0; i < iterationsPerStep; i++) {
                    sum += Math.sqrt(iterations) + Math.sin(iterations) + Math.cos(iterations);
                    iterations++;
                    if (iterations % 1000000 === 0) {
                        sum = sum % 1000000;
                    }
                }
                
                currentStep++;
                const progress = Math.floor((currentStep / totalSteps) * 100);
                
                // 更新进度
                document.getElementById('mainProgress').textContent = progress;
                document.getElementById('mainBatch').textContent = currentStep;
                document.getElementById('mainProgressBar').style.width = progress + '%';
                
                if (currentStep < totalSteps) {
                    // 立即执行下一批,造成持续卡顿
                    // 使用setTimeout(fn, 0)让浏览器有机会渲染,但仍然很卡
                    setTimeout(doCalculationBatch, 0);
                } else {
                    // 计算完成
                    const endTime = performance.now();
                    const duration = (endTime - startTime).toFixed(2);
                    
                    statusDiv.innerHTML = `
                        <div class="status-title">状态:完成 ✓</div>
                        <div class="status-content">
                            <span class="success">计算完成!</span><br>
                            执行迭代: <span class="highlight">${iterations.toLocaleString()}</span> 次<br>
                            计算结果: <span class="highlight">${sum.toFixed(2)}</span><br>
                            总耗时: <span class="highlight">${duration}ms</span> (约${(duration/1000).toFixed(1)}秒)<br>
                            <span style="color: #f5576c;">⚠️ 计算期间UI严重卡顿,FPS大幅下降</span>
                        </div>
                    `;
                }
            }
            
            // 开始第一批计算
            setTimeout(doCalculationBatch, 100);
        }

        // Web Worker 计算(不会阻塞UI)
        function calculateInWorker() {
            initWorker();
            
            const statusDiv = document.getElementById('workerStatus');
            statusDiv.innerHTML = `
                <div class="status-title">状态:启动中...</div>
                <div class="status-content">
                    正在启动Web Worker...<br>
                    <span style="color: #28a745;">✓ UI保持响应</span>
                </div>
            `;

            // 发送计算任务到 Worker(相同的计算量:1亿次迭代)
            worker.postMessage({ 
                totalIterations: 100000000,  // 1亿次迭代
                batchSize: 2000000  // 每批200万次,与主线程一致
            });
        }

        // 页面加载完成后初始化
        window.addEventListener('load', () => {
            console.log('Web Worker Demo 已加载');
            // 启动JavaScript动画
            startAnimation();
        });

        // 页面关闭时清理 Worker
        window.addEventListener('beforeunload', () => {
            if (worker) {
                worker.terminate();
            }
        });
    </script>
</body>
</html>

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions