🚀彻底掌握异步编程: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

结语

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

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

相关推荐
ze_juejin2 小时前
为什么说vue比Angular轻巧
前端
六月的可乐2 小时前
Vue3项目中集成AI对话功能的实战经验分享
前端·人工智能·openai
PineappleCoder2 小时前
面试官你好,请您听我“编解”!!!
前端·算法·面试
ze_juejin2 小时前
vue的选项式API和组合式API
前端
AAA_Tj2 小时前
CSS查漏补缺-BFC全面深入掌握
前端
是晓晓吖2 小时前
Page.waitForResponse的竞态条件与最佳实践
前端·puppeteer
猿如意2 小时前
vue项目的main.js规划设计与合理使用
前端·javascript·vue.js
海云前端12 小时前
前端性能优化面试:这样答稳过
前端
郑陈皮2 小时前
qiankun vs MicroApp 微前端框架对比分析
前端·前端框架