JavaScript事件循环与异步编程
·
事件循环基础
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最佳实践
-
使用try/catch进行错误处理
-
async函数中的错误需要使用try/catch捕获
-
避免未处理的Promise拒绝
-
-
并行执行异步操作
-
使用Promise.all()并行执行多个不相关的异步操作
-
显著提高性能,减少总等待时间
-
-
在循环中正确使用await
-
避免在循环中顺序等待,除非操作有依赖关系
-
使用Promise.all()并行处理数组中的异步操作
-
-
async函数作为Promise使用
-
async函数本质上返回Promise
-
可以与传统Promise方法混合使用
-
-
使用异步IIFE
-
在非模块环境中使用IIFE来使用await
-
ES2022支持在模块顶层使用await
-
更多推荐

所有评论(0)