事件循环基础

JavaScript的事件循环是其异步编程的核心机制,它允许JavaScript在单线程环境中处理并发操作。

html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JavaScript事件循环与异步编程</title>
    <style>
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            line-height: 1.6;
            color: #333;
            max-width: 1200px;
            margin: 0 auto;
            padding: 20px;
            background-color: #f5f7fa;
        }
        h1 {
            color: #2c3e50;
            text-align: center;
            margin-bottom: 30px;
        }
        h2 {
            color: #3498db;
            border-bottom: 2px solid #3498db;
            padding-bottom: 8px;
        }
        .container {
            display: flex;
            flex-wrap: wrap;
            gap: 20px;
            margin-bottom: 30px;
        }
        .card {
            background: white;
            border-radius: 8px;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
            padding: 20px;
            flex: 1;
            min-width: 300px;
        }
        .code-block {
            background-color: #2d3748;
            color: #e2e8f0;
            padding: 15px;
            border-radius: 6px;
            overflow-x: auto;
            font-family: 'Courier New', monospace;
            margin: 15px 0;
        }
        .btn {
            display: inline-block;
            background-color: #3498db;
            color: white;
            padding: 10px 15px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            margin: 5px;
            transition: background-color 0.3s;
        }
        .btn:hover {
            background-color: #2980b9;
        }
        .console {
            background-color: #1a202c;
            color: #68d391;
            padding: 15px;
            border-radius: 6px;
            height: 200px;
            overflow-y: auto;
            font-family: 'Courier New', monospace;
            margin-top: 15px;
        }
        .event-loop {
            display: flex;
            justify-content: space-between;
            margin: 20px 0;
        }
        .stack, .queue {
            background: white;
            border-radius: 8px;
            padding: 15px;
            width: 45%;
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
        }
        .stack-item, .queue-item {
            background: #3498db;
            color: white;
            padding: 8px 12px;
            margin: 5px 0;
            border-radius: 4px;
        }
    </style>
</head>
<body>
    <h1>JavaScript事件循环与异步编程</h1>
    
    <div class="container">
        <div class="card">
            <h2>事件循环原理</h2>
            <p>JavaScript是单线程语言,但通过事件循环机制实现了非阻塞的异步操作。</p>
            
            <div class="event-loop">
                <div class="stack">
                    <h3>调用栈 (Call Stack)</h3>
                    <div id="stack-items">
                        <div class="stack-item">主程序</div>
                    </div>
                </div>
                <div class="queue">
                    <h3>任务队列 (Task Queue)</h3>
                    <div id="queue-items"></div>
                </div>
            </div>
            
            <button class="btn" id="demo-btn">运行事件循环演示</button>
            <div class="console" id="console-output"></div>
        </div>
        
        <div class="card">
            <h2>5种更好的async/await使用方式</h2>
            
            <h3>1. 使用try/catch进行错误处理</h3>
            <div class="code-block">
async function fetchData() {
    try {
        const response = await fetch('/api/data');
        const data = await response.json();
        return data;
    } catch (error) {
        console.error('获取数据失败:', error);
        // 处理错误或返回默认值
        return { default: true };
    }
}
            </div>
            
            <h3>2. 并行执行异步操作</h3>
            <div class="code-block">
async function fetchAllData() {
    // 并行执行多个异步操作
    const [user, posts, notifications] = await Promise.all([
        fetch('/api/user').then(r => r.json()),
        fetch('/api/posts').then(r => r.json()),
        fetch('/api/notifications').then(r => r.json())
    ]);
    
    return { user, posts, notifications };
}
            </div>
            
            <h3>3. 在循环中正确使用await</h3>
            <div class="code-block">
// 错误方式 - 顺序执行,性能差
async function processItems(items) {
    for (const item of items) {
        await processItem(item); // 每次等待一个完成
    }
}

// 正确方式 - 并行执行
async function processItems(items) {
    // 所有异步操作同时开始
    const promises = items.map(item => processItem(item));
    // 等待所有完成
    await Promise.all(promises);
}
            </div>
            
            <h3>4. 使用async函数作为Promise使用</h3>
            <div class="code-block">
// async函数返回Promise,可以链式调用
fetchData()
    .then(data => {
        console.log('数据:', data);
        return processData(data);
    })
    .then(result => {
        console.log('处理结果:', result);
    })
    .catch(error => {
        console.error('发生错误:', error);
    });

// 或者与其他Promise结合
Promise.race([
    fetchData(),
    timeout(5000) // 5秒超时
]).then(result => {
    console.log('最先完成的结果:', result);
});
            </div>
            
            <h3>5. 使用异步IIFE(立即调用函数表达式)</h3>
            <div class="code-block">
// 在顶层使用await
(async function() {
    try {
        const data = await fetchData();
        const processed = await processData(data);
        console.log('最终结果:', processed);
    } catch (error) {
        console.error('执行失败:', error);
    }
})();

// 或者在模块中使用顶层await(ES2022)
// const data = await fetchData();
// console.log('数据:', data);
            </div>
            
            <button class="btn" id="async-demo-btn">运行async/await演示</button>
        </div>
    </div>
    
    <div class="card">
        <h2>实际应用示例</h2>
        <div class="code-block">
// 实际应用:带有错误处理和超时的API调用
async function robustAPICall(url, timeoutMs = 5000) {
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
    
    try {
        const response = await fetch(url, { signal: controller.signal });
        clearTimeout(timeoutId);
        
        if (!response.ok) {
            throw new Error(`HTTP错误: ${response.status}`);
        }
        
        return await response.json();
    } catch (error) {
        if (error.name === 'AbortError') {
            throw new Error('请求超时');
        }
        throw error;
    }
}

// 使用示例
(async () => {
    try {
        const data = await robustAPICall('https://api.example.com/data');
        console.log('获取的数据:', data);
    } catch (error) {
        console.error('API调用失败:', error.message);
    }
})();
        </div>
    </div>

    <script>
        // 事件循环演示
        document.getElementById('demo-btn').addEventListener('click', function() {
            const consoleOutput = document.getElementById('console-output');
            const stackItems = document.getElementById('stack-items');
            const queueItems = document.getElementById('queue-items');
            
            consoleOutput.innerHTML = '';
            
            function log(message) {
                consoleOutput.innerHTML += `> ${message}\n`;
                consoleOutput.scrollTop = consoleOutput.scrollHeight;
            }
            
            function updateStack(items) {
                stackItems.innerHTML = '';
                items.forEach(item => {
                    const div = document.createElement('div');
                    div.className = 'stack-item';
                    div.textContent = item;
                    stackItems.appendChild(div);
                });
            }
            
            function updateQueue(items) {
                queueItems.innerHTML = '';
                items.forEach(item => {
                    const div = document.createElement('div');
                    div.className = 'queue-item';
                    div.textContent = item;
                    queueItems.appendChild(div);
                });
            }
            
            log('程序开始执行');
            updateStack(['主程序']);
            
            // 模拟同步代码
            setTimeout(() => {
                log('同步代码执行完成');
                updateStack([]);
                updateQueue(['setTimeout回调']);
                
                // 模拟事件循环处理队列
                setTimeout(() => {
                    log('事件循环处理队列中的任务');
                    updateStack(['setTimeout回调']);
                    updateQueue([]);
                    
                    setTimeout(() => {
                        log('setTimeout回调执行完成');
                        updateStack([]);
                    }, 500);
                }, 500);
            }, 1000);
            
            // 模拟Promise
            setTimeout(() => {
                log('Promise创建并添加到微任务队列');
                updateQueue(['setTimeout回调', 'Promise回调']);
                
                // 模拟微任务优先执行
                setTimeout(() => {
                    log('微任务(Promise)优先于宏任务执行');
                    updateStack(['Promise回调']);
                    updateQueue(['setTimeout回调']);
                    
                    setTimeout(() => {
                        log('Promise回调执行完成');
                        updateStack([]);
                    }, 500);
                }, 500);
            }, 1500);
        });
        
        // async/await演示
        document.getElementById('async-demo-btn').addEventListener('click', async function() {
            const consoleOutput = document.getElementById('console-output');
            consoleOutput.innerHTML = '';
            
            function log(message) {
                consoleOutput.innerHTML += `> ${message}\n`;
                consoleOutput.scrollTop = consoleOutput.scrollHeight;
            }
            
            // 模拟异步函数
            function delay(ms, value) {
                return new Promise(resolve => setTimeout(() => {
                    log(`延迟 ${ms}ms 后完成,返回值: ${value}`);
                    resolve(value);
                }, ms));
            }
            
            // 演示并行执行
            log('开始并行执行多个异步操作');
            try {
                const startTime = Date.now();
                
                // 并行执行
                const results = await Promise.all([
                    delay(1000, '结果1'),
                    delay(1500, '结果2'),
                    delay(800, '结果3')
                ]);
                
                const endTime = Date.now();
                log(`所有操作完成,总耗时: ${endTime - startTime}ms`);
                log(`结果: ${results.join(', ')}`);
                
                // 演示顺序执行
                log('开始顺序执行异步操作');
                const sequentialStart = Date.now();
                
                const result1 = await delay(1000, '顺序结果1');
                const result2 = await delay(1000, '顺序结果2');
                const result3 = await delay(1000, '顺序结果3');
                
                const sequentialEnd = Date.now();
                log(`顺序执行完成,总耗时: ${sequentialEnd - sequentialStart}ms`);
                log(`结果: ${result1}, ${result2}, ${result3}`);
                
            } catch (error) {
                log(`发生错误: ${error}`);
            }
        });
    </script>
</body>
</html>

关键概念说明

1. 事件循环机制

  • JavaScript是单线程的,但通过事件循环处理异步操作

  • 调用栈执行同步代码

  • 异步操作完成后,回调函数被放入任务队列

  • 事件循环不断检查调用栈是否为空,然后从队列中取出任务执行

2. async/await最佳实践

  1. 使用try/catch进行错误处理

    • async函数中的错误需要使用try/catch捕获

    • 避免未处理的Promise拒绝

  2. 并行执行异步操作

    • 使用Promise.all()并行执行多个不相关的异步操作

    • 显著提高性能,减少总等待时间

  3. 在循环中正确使用await

    • 避免在循环中顺序等待,除非操作有依赖关系

    • 使用Promise.all()并行处理数组中的异步操作

  4. async函数作为Promise使用

    • async函数本质上返回Promise

    • 可以与传统Promise方法混合使用

  5. 使用异步IIFE

    • 在非模块环境中使用IIFE来使用await

    • ES2022支持在模块顶层使用await

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐