JavaScript 事件循环与异步编程详解
1. 事件循环基础概念
JavaScript 是单线程语言,但通过事件循环(Event Loop)机制实现了异步操作。事件循环包含以下关键组件:
- 调用栈(Call Stack)
- 任务队列(Task Queue/Callback Queue)
- 微任务队列(Microtask Queue)
- Web APIs(浏览器环境)
1.1 基本运行机制
console.log('1'); // 同步任务
setTimeout(() => {
console.log('2'); // 宏任务
}, 0);
Promise.resolve().then(() => {
console.log('3'); // 微任务
});
console.log('4');
// 输出顺序:1, 4, 3, 2
2. 宏任务(Macrotask)和微任务(Microtask)
2.1 宏任务
- setTimeout/setInterval
- setImmediate (Node.js)
- requestAnimationFrame
- I/O
- UI rendering
2.2 微任务
- Promise.then/catch/finally
- process.nextTick (Node.js)
- MutationObserver
- queueMicrotask()
// 宏任务和微任务的执行顺序示例
console.log('script start'); // 1
setTimeout(() => {
console.log('setTimeout'); // 5
}, 0);
Promise.resolve()
.then(() => {
console.log('promise1'); // 3
})
.then(() => {
console.log('promise2'); // 4
});
console.log('script end'); // 2
// 输出顺序:
// script start
// script end
// promise1
// promise2
// setTimeout
3. Promise 详解
3.1 基本用法
const promise = new Promise((resolve, reject) => {
// 异步操作
setTimeout(() => {
const random = Math.random();
if (random > 0.5) {
resolve('成功');
} else {
reject('失败');
}
}, 1000);
});
promise
.then(result => console.log(result))
.catch(error => console.error(error))
.finally(() => console.log('完成'));
3.2 Promise 链式调用
function asyncOperation(value) {
return new Promise(resolve => {
setTimeout(() => {
resolve(value * 2);
}, 1000);
});
}
asyncOperation(2)
.then(result => {
console.log(result); // 4
return asyncOperation(result);
})
.then(result => {
console.log(result); // 8
return asyncOperation(result);
})
.then(result => {
console.log(result); // 16
});
4. Async/Await
4.1 基本用法
async function fetchUserData() {
try {
const response = await fetch('https://api.example.com/user');
const data = await response.json();
return data;
} catch (error) {
console.error('获取用户数据失败:', error);
throw error;
}
}
4.2 并行执行
async function fetchMultipleData() {
try {
// 并行执行多个异步操作
const [users, posts] = await Promise.all([
fetch('https://api.example.com/users').then(r => r.json()),
fetch('https://api.example.com/posts').then(r => r.json())
]);
return { users, posts };
} catch (error) {
console.error('获取数据失败:', error);
throw error;
}
}
5. 实际应用示例
5.1 异步请求管理
class RequestManager {
constructor() {
this.queue = new Map();
}
async request(key, promiseFactory) {
if (this.queue.has(key)) {
return this.queue.get(key);
}
try {
const promise = promiseFactory();
this.queue.set(key, promise);
const result = await promise;
return result;
} finally {
this.queue.delete(key);
}
}
}
// 使用示例
const requestManager = new RequestManager();
async function fetchUserData(userId) {
return requestManager.request(
`user_${userId}`,
() => fetch(`https://api.example.com/users/${userId}`).then(r => r.json())
);
}
5.2 异步任务队列
class AsyncQueue {
constructor() {
this.queue = [];
this.running = false;
}
async add(task) {
return new Promise((resolve, reject) => {
this.queue.push({ task, resolve, reject });
this.process();
});
}
async process() {
if (this.running) return;
this.running = true;
while (this.queue.length > 0) {
const { task, resolve, reject } = this.queue.shift();
try {
const result = await task();
resolve(result);
} catch (error) {
reject(error);
}
}
this.running = false;
}
}
// 使用示例
const queue = new AsyncQueue();
async function example() {
await queue.add(async () => {
await new Promise(resolve => setTimeout(resolve, 1000));
return '任务1完成';
});
await queue.add(async () => {
await new Promise(resolve => setTimeout(resolve, 500));
return '任务2完成';
});
}
6. 错误处理和最佳实践
6.1 错误处理
async function robustFetch(url, options = {}) {
const MAX_RETRIES = 3;
let lastError;
for (let i = 0; i < MAX_RETRIES; i++) {
try {
const response = await fetch(url, {
...options,
timeout: 5000
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
lastError = error;
console.warn(`Attempt ${i + 1} failed:`, error);
await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, i)));
}
}
throw lastError;
}
6.2 性能优化
// 使用 AbortController 取消请求
async function fetchWithTimeout(url, timeout = 5000) {
const controller = new AbortController();
const id = setTimeout(() => controller.abort(), timeout);
try {
const response = await fetch(url, {
signal: controller.signal
});
const data = await response.json();
return data;
} catch (error) {
if (error.name === 'AbortError') {
throw new Error('Request timed out');
}
throw error;
} finally {
clearTimeout(id);
}
}
总结
理解事件循环和异步编程对于开发高质量的 JavaScript 应用至关重要。关键点包括:
- 掌握宏任务和微任务的执行顺序
- 合理使用 Promise 和 async/await
- 实现可靠的错误处理机制
- 注意性能优化和资源管理
- 避免回调地狱,保持代码可读性
在实际开发中,建议:
- 优先使用 async/await 而不是回调
- 合理处理并发请求
- 实现适当的超时和重试机制
- 注意内存泄漏问题
- 保持代码的可测试性