90%的开发者都会踩的异步编程坑,你中了几个?本文不仅修复原有问题,更用20个实战案例带你彻底避开这些陷阱!
开头钩子:为什么你需要这篇指南?
相信每个前端开发者都经历过这样的痛苦:项目中的异步操作越来越多,回调地狱越来越深,代码可读性急剧下降。传统的Promise虽然解决了回调地狱的问题,但在复杂异步流程控制方面仍然力不从心。
而async/await和Generator的出现,彻底改变了这一局面。它们不仅让异步代码看起来像同步代码一样清晰,更为我们提供了前所未有的异步控制能力。但在实际开发中,我发现很多开发者仍然会遇到各种奇怪的问题:内存泄漏、性能瓶颈、难以调试的bug...
基础概念快速回顾
在深入案例之前,我们先快速回顾一下这些核心概念:
- async/await: ES2017引入的语法糖,让异步代码的书写和阅读变得更简单
- Generator函数: 可以暂停和恢复执行的函数,通过yield关键字控制
- next()方法: 用于控制Generator函数的执行流程
下面让我们通过20个实战案例来深入理解这些技术。
案例1:基础async/await使用
javascript
// 模拟异步获取用户数据
async function getUserData(userId) {
try {
console.log('开始获取用户数据...');
const response = await fetch(`/api/users/${userId}`);
const userData = await response.json();
console.log('用户数据获取成功:', userData);
return userData;
} catch (error) {
console.error('获取用户数据失败:', error);
throw error;
}
}
// 使用示例
getUserData(123)
.then(data => console.log('最终用户数据:', data))
.catch(error => console.error('处理失败:', error));
// 完整的fetch mock实现
const mockFetch = (url, options = {}) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() < 0.1) {
reject(new Error(`Network error for ${url}`));
} else {
resolve({
ok: true,
json: () => Promise.resolve({ data: `Response from ${url}` }),
text: () => Promise.resolve(`Text response from ${url}`)
});
}
}, 100 + Math.random() * 400);
});
};
技术要点: await只能在async函数中使用,它会暂停函数执行,直到Promise解决。
案例2:错误处理最佳实践
javascript
// 基于try-catch的分层错误处理 + 重试机制:
class AdvancedErrorHandler {
static async withRetry(operation, maxRetries = 3, delay = 1000) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await operation();
} catch (error) {
if (attempt === maxRetries) throw error;
console.warn(`Attempt ${attempt} failed, retrying in ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay * attempt));
}
}
}
static async withFallback(mainOperation, fallbackOperation) {
try {
return await mainOperation();
} catch (error) {
console.warn('Main operation failed, using fallback:', error.message);
return await fallbackOperation();
}
}
}
// 普通方式:
async function fetchWithRetry(url, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
const response = await fetch(url);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return await response.json();
} catch (error) {
if (i === retries - 1) throw error;
console.log(`第${i + 1}次尝试失败,等待重试...`);
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
}
}
}
// 使用示例
fetchWithRetry('https://api.example.com/data')
.then(data => console.log('获取成功:', data))
.catch(error => console.error('最终失败:', error));
案例3:并行执行优化
javascript
async function loadMultipleResources() {
const [userData, posts, comments] = await Promise.all([
fetch('/api/user/1').then(r => r.json()),
fetch('/api/posts').then(r => r.json()),
fetch('/api/comments').then(r => r.json())
]);
console.log('所有数据加载完成');
return { userData, posts, comments };
}
// 使用示例
loadMultipleResources()
.then(result => console.log('合并结果:', result));
案例4:顺序执行控制
javascript
async function sequentialOperations() {
const step1 = await doFirstThing();
console.log('第一步完成:', step1);
const step2 = await doSecondThing(step1);
console.log('第二步完成:', step2);
const step3 = await doThirdThing(step2);
console.log('第三步完成:', step3);
return step3;
}
// 辅助函数
async function doFirstThing() { /* ... */ }
async function doSecondThing(input) { /* ... */ }
async function doThirdThing(input) { /* ... */ }
案例5:Generator基础应用
javascript
function* simpleGenerator() {
console.log('开始执行');
yield '第一个值';
console.log('继续执行');
yield '第二个值';
console.log('执行结束');
return '最终值';
}
// 使用示例
const gen = simpleGenerator();
console.log(gen.next()); // { value: '第一个值', done: false }
console.log(gen.next()); // { value: '第二个值', done: false }
console.log(gen.next()); // { value: '最终值', done: true }
案例6:Generator与异步结合
javascript
async function* asyncGenerator() {
try {
const data1 = await fetchData1();
yield { type: 'data1', data: data1 };
const data2 = await fetchData2(data1);
yield { type: 'data2', data: data2 };
const data3 = await processData(data2);
yield { type: 'result', data: data3 };
} catch (error) {
yield { type: 'error', error };
}
}
// 使用示例
const agen = asyncGenerator();
agen.next().then(({ value }) => console.log('第一步:', value));
案例7:自定义迭代器
javascript
function createRangeIterator(start, end, step = 1) {
let current = start;
return {
[Symbol.iterator]() {
return this;
},
next() {
if (current <= end) {
const value = current;
current += step;
return { value, done: false };
}
return { value: undefined, done: true };
}
};
}
// 使用示例
const iterator = createRangeIterator(1, 5);
for (const num of iterator) {
console.log(num); // 1, 2, 3, 4, 5
}
案例8:状态机实现
javascript
function* trafficLight() {
while (true) {
console.log('红灯 - 停止');
yield 'red';
console.log('黄灯 - 准备');
yield 'yellow';
console.log('绿灯 - 通行');
yield 'green';
console.log('黄灯 - 警告');
yield 'yellow';
}
}
// 使用示例
const light = trafficLight();
setInterval(() => {
console.log('当前状态:', light.next().value);
}, 3000);
案例9:数据流处理
javascript
async function* processDataStream(stream) {
for await (const chunk of stream) {
try {
const processed = await processChunk(chunk);
yield processed;
} catch (error) {
console.error('处理块失败:', error);
yield null;
}
}
}
// 使用示例
async function handleStream() {
const stream = getDataStream();
for await (const processedData of processDataStream(stream)) {
if (processedData) {
console.log('处理后的数据:', processedData);
}
}
}
案例10:复杂异步流程控制
javascript
async function complexWorkflow() {
try {
// 阶段1: 数据准备
const [user, config] = await Promise.all([
fetchUser(),
fetchConfig()
]);
// 阶段2: 验证和处理
if (!user.isValid) throw new Error('用户无效');
const processedData = await processUserData(user, config);
// 阶段3: 并行执行多个任务
const [result1, result2, result3] = await Promise.all([
task1(processedData),
task2(processedData),
task3(processedData)
]);
// 阶段4: 最终处理
const finalResult = await combineResults(result1, result2, result3);
return finalResult;
} catch (error) {
console.error('工作流执行失败:', error);
await rollbackOperations();
throw error;
}
}
案例11:异步限流控制
javascript
class AsyncRateLimiter {
constructor(maxConcurrent = 5) {
this.maxConcurrent = maxConcurrent;
this.current = 0;
this.queue = [];
}
async execute(operation) {
if (this.current >= this.maxConcurrent) {
await new Promise(resolve => this.queue.push(resolve));
}
this.current++;
try {
return await operation();
} finally {
this.current--;
if (this.queue.length > 0) {
this.queue.shift()();
}
}
}
}
// 使用示例
const limiter = new AsyncRateLimiter(3);
await Promise.all(urls.map(url =>
limiter.execute(() => fetchData(url))
));
案例12:可取消的异步操作
javascript
function createCancellableAsync(operation) {
let cancelled = false;
let cancelReason = null;
const promise = operation().catch(error => {
if (cancelled) {
throw new Error(`Operation cancelled: ${cancelReason}`);
}
throw error;
});
return {
promise,
cancel: (reason = 'User cancelled') => {
cancelled = true;
cancelReason = reason;
}
};
}
// 使用示例
const { promise, cancel } = createCancellableAsync(async () => {
await someLongRunningOperation();
});
// 在需要时调用 cancel()
案例13:异步状态管理
javascript
class AsyncStateMachine {
constructor() {
this.state = 'idle';
this.queue = [];
}
async setState(newState, transition) {
if (this.state !== 'idle') {
await new Promise(resolve => this.queue.push(resolve));
}
this.state = 'transitioning';
try {
await transition();
this.state = newState;
} catch (error) {
this.state = 'error';
throw error;
} finally {
if (this.queue.length > 0) {
this.queue.shift()();
}
}
}
}
案例14:优先级异步队列
javascript
class PriorityAsyncQueue {
constructor() {
this.highPriority = [];
this.normalPriority = [];
this.processing = false;
}
async add(operation, priority = 'normal') {
const queue = priority === 'high' ? this.highPriority : this.normalPriority;
queue.push(operation);
if (!this.processing) {
this.processing = true;
await this.process();
}
}
async process() {
while (this.highPriority.length > 0 || this.normalPriority.length > 0) {
const operation = this.highPriority.shift() || this.normalPriority.shift();
try {
await operation();
} catch (error) {
console.error('Queue operation failed:', error);
}
}
this.processing = false;
}
}
案例15:异步缓存策略
javascript
class AsyncCache {
constructor(ttl = 300000) { // 5分钟默认TTL
this.cache = new Map();
this.ttl = ttl;
}
async get(key, loader) {
const cached = this.cache.get(key);
if (cached && Date.now() - cached.timestamp < this.ttl) {
return cached.value;
}
// 防止缓存击穿:只允许一个请求加载数据
if (cached && cached.loading) {
return cached.loading;
}
const loadingPromise = loader();
this.cache.set(key, {
value: null,
loading: loadingPromise,
timestamp: Date.now()
});
try {
const value = await loadingPromise;
this.cache.set(key, { value, loading: null, timestamp: Date.now() });
return value;
} catch (error) {
this.cache.delete(key);
throw error;
}
}
}
案例16:异步事务处理
javascript
class AsyncTransaction {
constructor() {
this.operations = [];
this.compensations = [];
}
add(operation, compensation) {
this.operations.push(operation);
this.compensations.push(compensation);
}
async execute() {
const results = [];
let executed = 0;
try {
for (const operation of this.operations) {
const result = await operation();
results.push(result);
executed++;
}
return results;
} catch (error) {
// 执行补偿操作
for (let i = executed - 1; i >= 0; i--) {
try {
await this.compensations[i]();
} catch (compError) {
console.error(`Compensation failed for operation ${i}:`, compError);
}
}
throw error;
}
}
}
案例17:异步事件总线
javascript
class AsyncEventBus {
constructor() {
this.handlers = new Map();
this.middlewares = [];
}
on(event, handler) {
if (!this.handlers.has(event)) {
this.handlers.set(event, []);
}
this.handlers.get(event).push(handler);
}
use(middleware) {
this.middlewares.push(middleware);
}
async emit(event, data) {
const handlers = this.handlers.get(event) || [];
let currentData = data;
for (const middleware of this.middlewares) {
currentData = await middleware(currentData, event);
}
const results = [];
for (const handler of handlers) {
try {
const result = await handler(currentData);
results.push(result);
} catch (error) {
console.error(`Handler failed for event ${event}:`, error);
}
}
return results;
}
}
案例18:异步数据流水线
javascript
class AsyncPipeline {
constructor() {
this.stages = [];
}
add(stage) {
this.stages.push(stage);
return this;
}
async execute(input) {
let result = input;
for (const stage of this.stages) {
result = await stage(result);
}
return result;
}
static create() {
return new AsyncPipeline();
}
}
// 使用示例
const pipeline = AsyncPipeline.create()
.add(async data => await validate(data))
.add(async data => await transform(data))
.add(async data => await persist(data));
const result = await pipeline.execute(rawData);
案例19:异步工作池
javascript
class AsyncWorkerPool {
constructor(workerCount, workerFactory) {
this.workers = Array.from({ length: workerCount }, workerFactory);
this.taskQueue = [];
this.availableWorkers = [...this.workers];
}
async execute(task) {
if (this.availableWorkers.length === 0) {
await new Promise(resolve => this.taskQueue.push(resolve));
}
const worker = this.availableWorkers.pop();
try {
return await worker(task);
} finally {
this.availableWorkers.push(worker);
if (this.taskQueue.length > 0) {
this.taskQueue.shift()();
}
}
}
async executeAll(tasks) {
return Promise.all(tasks.map(task => this.execute(task)));
}
}
案例20:异步监控和性能追踪
javascript
class AsyncMonitor {
constructor() {
this.metrics = new Map();
this.listeners = new Set();
}
async track(operation, name = 'unknown') {
const startTime = Date.now();
const startMem = process.memoryUsage?.().rss || 0;
try {
const result = await operation();
const duration = Date.now() - startTime;
const memUsage = process.memoryUsage?.().rss - startMem || 0;
this.recordMetric(name, { duration, memUsage, success: true });
return result;
} catch (error) {
const duration = Date.now() - startTime;
this.recordMetric(name, { duration, success: false, error: error.message });
throw error;
}
}
recordMetric(name, metric) {
if (!this.metrics.has(name)) {
this.metrics.set(name, []);
}
this.metrics.get(name).push(metric);
// 通知监听器
for (const listener of this.listeners) {
listener(name, metric);
}
}
onMetric(listener) {
this.listeners.add(listener);
}
getStats(name) {
const metrics = this.metrics.get(name) || [];
// 返回统计信息...
}
}
⚠️ 异步编程常见陷阱与解决方案
陷阱1:微任务队列阻塞
javascript
// ❌ 错误示例:阻塞微任务队列
async function blockingExample() {
const results = [];
for (let i = 0; i < 1000; i++) {
results.push(await someAsyncOperation(i)); // 逐个等待,性能极差
}
return results;
}
// ✅ 正确做法:使用Promise.all
async function optimizedExample() {
const promises = [];
for (let i = 0; i < 1000; i++) {
promises.push(someAsyncOperation(i));
}
return Promise.all(promises);
}
陷阱2:Generator内存泄漏
javascript
// ❌ 错误示例:未关闭的Generator
function* leakyGenerator() {
let resource = acquireExpensiveResource();
try {
while (true) {
yield process(resource);
}
} finally {
// 容易被忘记的资源释放
releaseResource(resource);
}
}
// ✅ 正确做法:使用Symbol.dispose(ES2022)
function* safeGenerator() {
using resource = acquireExpensiveResource(); // 自动释放
while (true) {
yield process(resource);
}
}
陷阱3:并行执行错误处理
javascript
// ❌ 错误示例:Promise.all中的错误会终止所有操作
async function riskyParallel() {
try {
const [a, b, c] = await Promise.all([
riskyOperationA(),
riskyOperationB(),
riskyOperationC()
]);
return { a, b, c };
} catch (error) {
// 任何一个失败都会进入这里
console.error('One operation failed:', error);
throw error;
}
}
// ✅ 正确做法:使用Promise.allSettled
async function safeParallel() {
const results = await Promise.allSettled([
riskyOperationA(),
riskyOperationB(),
riskyOperationC()
]);
const successful = results.filter(r => r.status === 'fulfilled');
const failed = results.filter(r => r.status === 'rejected');
if (failed.length > 0) {
console.warn(`${failed.length} operations failed:`, failed);
}
return successful.map(r => r.value);
}
🎯 最佳实践总结
- 错误处理:始终使用try-catch,考虑重试和降级策略
- 性能优化:合理使用并行执行,避免不必要的await
- 资源管理:及时释放Generator和异步操作的资源
- 内存安全:注意闭包和长期引用导致的内存泄漏
- 调试技巧:使用async stack traces和性能监控
- 并行操作:对于并行操作,优先使用Promise.all
📊 浏览器兼容性提示
- async/await: ES2017+,现代浏览器全面支持
- Generator: ES2015+,IE不支持
- 推荐使用Babel转译和合适的polyfill
结语
掌握这些技术,你将能够优雅地处理任何复杂的异步场景,真正告别回调地狱!
希望这篇详细的解析和实战案例对你有帮助。如果有任何问题,欢迎在评论区讨论