🚀彻底掌握异步编程:async/await + Generator 深度解析与20个实战案例

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);
}

🎯 最佳实践总结

  1. 错误处理:始终使用try-catch,考虑重试和降级策略
  2. 性能优化:合理使用并行执行,避免不必要的await
  3. 资源管理:及时释放Generator和异步操作的资源
  4. 内存安全:注意闭包和长期引用导致的内存泄漏
  5. 调试技巧:使用async stack traces和性能监控
  6. 并行操作:对于并行操作,优先使用Promise.all

📊 浏览器兼容性提示

  • async/await: ES2017+,现代浏览器全面支持
  • Generator: ES2015+,IE不支持
  • 推荐使用Babel转译和合适的polyfill

结语

掌握这些技术,你将能够优雅地处理任何复杂的异步场景,真正告别回调地狱!

希望这篇详细的解析和实战案例对你有帮助。如果有任何问题,欢迎在评论区讨论

相关推荐
@大迁世界16 小时前
TypeScript 的本质并非类型,而是信任
开发语言·前端·javascript·typescript·ecmascript
GIS之路16 小时前
GDAL 实现矢量裁剪
前端·python·信息可视化
是一个Bug16 小时前
后端开发者视角的前端开发面试题清单(50道)
前端
Amumu1213816 小时前
React面向组件编程
开发语言·前端·javascript
持续升级打怪中17 小时前
Vue3 中虚拟滚动与分页加载的实现原理与实践
前端·性能优化
GIS之路17 小时前
GDAL 实现矢量合并
前端
hxjhnct17 小时前
React useContext的缺陷
前端·react.js·前端框架
冰暮流星17 小时前
javascript逻辑运算符
开发语言·javascript·ecmascript
前端 贾公子17 小时前
从入门到实践:前端 Monorepo 工程化实战(4)
前端
菩提小狗17 小时前
Sqlmap双击运行脚本,双击直接打开。
前端·笔记·安全·web安全