未找到匹配的笔记

1.31 - async/await原理、Generator

JavaScript面试

JavaScript async/await 与 Generator 深度解析

目录

  1. 前置知识:异步编程的演进
  2. Generator 函数深度剖析
  3. async/await 原理解析
  4. 底层实现与源码分析
  5. 常见面试题与最佳实践
  6. 实战应用场景

前置知识:异步编程的演进

1.1 JavaScript 异步发展史

// 第一代:回调函数(Callback)
setTimeout(function() {
  console.log('延迟执行');
}, 1000);

// 第二代:Promise
new Promise((resolve, reject) => {
  setTimeout(() => resolve('成功'), 1000);
}).then(result => console.log(result));

// 第三代:Generator(ES6)
function* gen() {
  const result = yield fetchData();
  console.log(result);
}

// 第四代:async/await(ES2017)
async function fetchUser() {
  const user = await getUserData();
  return user;
}

1.2 为什么需要 Generator 和 async/await?

回调地狱问题:

// 回调地狱示例
getUserId(function(userId) {
  getUserData(userId, function(userData) {
    getUserOrders(userData.id, function(orders) {
      getOrderDetails(orders[0].id, function(details) {
        console.log(details);
        // 继续嵌套...
      });
    });
  });
});

解决方案对比:

// Promise 链式调用
getUserId()
  .then(userId => getUserData(userId))
  .then(userData => getUserOrders(userData.id))
  .then(orders => getOrderDetails(orders[0].id))
  .then(details => console.log(details))
  .catch(error => console.error(error));

// async/await(最优雅)
async function fetchUserOrders() {
  try {
    const userId = await getUserId();
    const userData = await getUserData(userId);
    const orders = await getUserOrders(userData.id);
    const details = await getOrderDetails(orders[0].id);
    console.log(details);
  } catch (error) {
    console.error(error);
  }
}

Generator 函数深度剖析

2.1 Generator 基本概念

Generator 是 ES6 引入的一种可以暂停执行的函数,它返回一个迭代器对象。

// 基本语法
function* generatorFunction() {
  console.log('开始执行');
  yield 'first';
  console.log('继续执行');
  yield 'second';
  console.log('最后执行');
  return 'done';
}

// 使用
const gen = generatorFunction(); // 不会立即执行

console.log(gen.next()); // { value: 'first', done: false }
console.log(gen.next()); // { value: 'second', done: false }
console.log(gen.next()); // { value: 'done', done: true }

关键特性:

  • 使用 function* 声明
  • 内部使用 yield 暂停执行
  • 调用后返回迭代器对象
  • 通过 next() 方法恢复执行

2.2 Generator 执行机制

function* example() {
  console.log('Step 1');
  const a = yield 1;
  console.log('Step 2, a =', a);
  const b = yield 2;
  console.log('Step 3, b =', b);
  return 3;
}

const iter = example();

// 第一次调用 next()
console.log(iter.next());
// 输出: "Step 1"
// 返回: { value: 1, done: false }

// 第二次调用 next(10)
console.log(iter.next(10));
// 输出: "Step 2, a = 10"
// 返回: { value: 2, done: false }

// 第三次调用 next(20)
console.log(iter.next(20));
// 输出: "Step 3, b = 20"
// 返回: { value: 3, done: true }

执行流程图:

调用 example()          → 返回迭代器(未执行函数体)
第一次 next()          → 执行到第一个 yield,返回 1
第二次 next(10)        → 将 10 赋值给 a,执行到第二个 yield,返回 2
第三次 next(20)        → 将 20 赋值给 b,执行到 return,返回 3

2.3 yield 的双向通信

function* dataFlow() {
  const input1 = yield 'What is your name?';
  const input2 = yield `Hello ${input1}, how old are you?`;
  return `${input1} is ${input2} years old`;
}

const conversation = dataFlow();

console.log(conversation.next().value);      // "What is your name?"
console.log(conversation.next('Alice').value); // "Hello Alice, how old are you?"
console.log(conversation.next(25).value);     // "Alice is 25 years old"

关键理解点:

  • yield 右侧的值会通过 next() 返回给外部
  • 下一次 next(value) 的参数会赋值给上一个 yield 表达式
  • 第一次 next() 的参数会被忽略(因为没有上一个 yield)

2.4 Generator 的高级用法

2.4.1 yield* 委托

function* inner() {
  yield 'inner-1';
  yield 'inner-2';
}

function* outer() {
  yield 'outer-1';
  yield* inner(); // 委托给另一个 Generator
  yield 'outer-2';
}

const gen = outer();
console.log([...gen]); 
// ['outer-1', 'inner-1', 'inner-2', 'outer-2']

2.4.2 错误处理

function* errorHandler() {
  try {
    yield 'start';
    yield 'middle';
  } catch (e) {
    console.log('捕获错误:', e);
  }
  yield 'end';
}

const gen = errorHandler();
console.log(gen.next());           // { value: 'start', done: false }
console.log(gen.throw('出错了!')); // 捕获错误: 出错了!
                                   // { value: 'end', done: false }

2.4.3 提前结束

function* cancellable() {
  try {
    yield 1;
    yield 2;
    yield 3;
  } finally {
    console.log('清理资源');
  }
}

const gen = cancellable();
console.log(gen.next());   // { value: 1, done: false }
console.log(gen.return()); // 清理资源
                           // { value: undefined, done: true }

2.5 Generator 实现异步控制流

// 手动执行 Generator 处理异步
function* fetchUser() {
  const user = yield fetch('https://api.example.com/user');
  const orders = yield fetch(`https://api.example.com/orders/${user.id}`);
  return { user, orders };
}

// 执行器函数
function run(generatorFunc) {
  const gen = generatorFunc();
  
  function step(nextValue) {
    const result = gen.next(nextValue);
    
    if (result.done) {
      return result.value;
    }
    
    // 假设 yield 返回的是 Promise
    result.value.then(
      value => step(value),
      error => gen.throw(error)
    );
  }
  
  step();
}

// 使用
run(fetchUser);

async/await 原理解析

3.1 async/await 本质

核心概念:async/await 是 Generator + Promise 的语法糖

// async/await 写法
async function fetchData() {
  const data1 = await fetch('/api/data1');
  const data2 = await fetch('/api/data2');
  return [data1, data2];
}

// 等价的 Generator 实现
function* fetchDataGenerator() {
  const data1 = yield fetch('/api/data1');
  const data2 = yield fetch('/api/data2');
  return [data1, data2];
}

// 需要配合执行器
function runGenerator(gen) {
  return new Promise((resolve, reject) => {
    const g = gen();
    
    function step(nextValue) {
      let result;
      try {
        result = g.next(nextValue);
      } catch (e) {
        return reject(e);
      }
      
      if (result.done) {
        return resolve(result.value);
      }
      
      Promise.resolve(result.value).then(
        value => step(value),
        error => {
          try {
            step(g.throw(error));
          } catch (e) {
            reject(e);
          }
        }
      );
    }
    
    step();
  });
}

// 使用
runGenerator(fetchDataGenerator);

3.2 async 函数的特性

// 1. async 函数总是返回 Promise
async function test1() {
  return 'hello';
}
console.log(test1()); // Promise { 'hello' }

// 2. 等价于
function test2() {
  return Promise.resolve('hello');
}

// 3. throw 错误会返回 rejected Promise
async function test3() {
  throw new Error('出错了');
}
test3().catch(e => console.log(e)); // Error: 出错了

// 4. async 函数内部的 return 值会成为 then 的参数
async function test4() {
  return { name: 'Alice', age: 25 };
}
test4().then(data => console.log(data)); // { name: 'Alice', age: 25 }

3.3 await 的执行机制

async function example() {
  console.log('1');
  
  const result1 = await Promise.resolve('2');
  console.log(result1);
  
  const result2 = await Promise.resolve('3');
  console.log(result2);
  
  console.log('4');
}

console.log('0');
example();
console.log('5');

// 输出顺序: 0, 1, 5, 2, 3, 4

执行流程分析:

1. 同步代码: console.log('0')
2. 调用 example()
3. 同步代码: console.log('1')
4. 遇到 await,暂停,将后续代码放入微任务队列
5. 跳出函数,继续执行: console.log('5')
6. 主线程执行完毕,执行微任务队列
7. 恢复 example 执行: console.log('2')
8. 再次遇到 await,暂停
9. 执行下一个微任务: console.log('3')
10. 最后执行: console.log('4')

3.4 await 后面跟不同类型值

async function awaitTest() {
  // 1. await Promise
  const p1 = await Promise.resolve(1);
  console.log(p1); // 1
  
  // 2. await thenable 对象
  const thenable = {
    then(resolve, reject) {
      resolve(2);
    }
  };
  const p2 = await thenable;
  console.log(p2); // 2
  
  // 3. await 普通值(会被包装成 Promise.resolve)
  const p3 = await 3;
  console.log(p3); // 3
  
  // 4. await rejected Promise
  try {
    await Promise.reject('error');
  } catch (e) {
    console.log(e); // 'error'
  }
}

3.5 错误处理机制

// 方式1: try-catch
async function method1() {
  try {
    const data = await fetch('/api/data');
    return data;
  } catch (error) {
    console.error('请求失败:', error);
    return null;
  }
}

// 方式2: Promise catch
async function method2() {
  const data = await fetch('/api/data').catch(error => {
    console.error('请求失败:', error);
    return null;
  });
  return data;
}

// 方式3: 统一错误处理
function asyncWrapper(fn) {
  return function(...args) {
    return fn(...args).catch(error => {
      console.error('异步错误:', error);
      // 可以上报错误到监控系统
      throw error;
    });
  };
}

const safeRequest = asyncWrapper(async function() {
  const data = await fetch('/api/data');
  return data;
});

底层实现与源码分析

4.1 手写 Generator 执行器(co 库原理)

/**
 * 自动执行 Generator 函数的执行器
 * 类似于 co 库的简化实现
 */
function co(generatorFunc) {
  return new Promise((resolve, reject) => {
    const gen = generatorFunc();
    
    function step(nextValue) {
      let result;
      
      // 执行 next() 并捕获可能的错误
      try {
        result = gen.next(nextValue);
      } catch (e) {
        return reject(e);
      }
      
      // 如果 Generator 执行完毕
      if (result.done) {
        return resolve(result.value);
      }
      
      // 将 yield 的值转换为 Promise
      Promise.resolve(result.value)
        .then(
          value => step(value),      // 成功时继续下一步
          error => {                 // 失败时抛入 Generator
            let result;
            try {
              result = gen.throw(error);
            } catch (e) {
              return reject(e);
            }
            step(result.value);
          }
        );
    }
    
    step(); // 启动执行
  });
}

// 使用示例
function* fetchData() {
  const user = yield fetch('/api/user');
  const orders = yield fetch(`/api/orders/${user.id}`);
  return { user, orders };
}

co(fetchData)
  .then(result => console.log(result))
  .catch(error => console.error(error));

4.2 手写 async/await(Babel 转译原理)

/**
 * 模拟 Babel 如何转译 async/await
 */

// 原始 async 函数
async function asyncFunc() {
  const result1 = await promise1();
  const result2 = await promise2(result1);
  return result2;
}

// Babel 转译后的代码(简化版)
function asyncFunc() {
  return _asyncToGenerator(function* () {
    const result1 = yield promise1();
    const result2 = yield promise2(result1);
    return result2;
  })();
}

// _asyncToGenerator 辅助函数
function _asyncToGenerator(generatorFunc) {
  return function() {
    const gen = generatorFunc.apply(this, arguments);
    
    return new Promise((resolve, reject) => {
      function step(key, arg) {
        let result;
        
        try {
          result = gen[key](arg); // gen.next(arg) 或 gen.throw(arg)
        } catch (error) {
          return reject(error);
        }
        
        const { value, done } = result;
        
        if (done) {
          return resolve(value);
        }
        
        // 确保 value 是 Promise
        return Promise.resolve(value).then(
          val => step('next', val),
          err => step('throw', err)
        );
      }
      
      step('next');
    });
  };
}

4.3 完整的 async/await polyfill

/**
 * 生产级别的 async/await 实现
 * 包含完整的错误处理和边界情况
 */
function asyncToGenerator(generatorFunc) {
  return function() {
    const self = this;
    const args = arguments;
    
    return new Promise((resolve, reject) => {
      const gen = generatorFunc.apply(self, args);
      
      function _next(value) {
        asyncGeneratorStep(gen, resolve, reject, _next, _throw, 'next', value);
      }
      
      function _throw(error) {
        asyncGeneratorStep(gen, resolve, reject, _next, _throw, 'throw', error);
      }
      
      _next(undefined);
    });
  };
}

function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
  let info, value;
  
  try {
    info = gen[key](arg);
    value = info.value;
  } catch (error) {
    reject(error);
    return;
  }
  
  if (info.done) {
    resolve(value);
  } else {
    Promise.resolve(value).then(_next, _throw);
  }
}

// 使用示例
const asyncFunc = asyncToGenerator(function* () {
  const result1 = yield fetch('/api/data1');
  const result2 = yield fetch('/api/data2');
  return [result1, result2];
});

asyncFunc().then(console.log).catch(console.error);

4.4 Generator 状态机原理

/**
 * Generator 函数的状态机实现原理
 */

// 原始 Generator
function* simpleGenerator() {
  yield 1;
  yield 2;
  return 3;
}

// 转换为状态机(简化版)
function simpleGeneratorStateMachine() {
  let state = 0; // 当前状态
  
  return {
    next(value) {
      switch (state) {
        case 0:
          state = 1;
          return { value: 1, done: false };
        case 1:
          state = 2;
          return { value: 2, done: false };
        case 2:
          state = 3;
          return { value: 3, done: true };
        default:
          return { value: undefined, done: true };
      }
    },
    
    throw(error) {
      throw error;
    },
    
    return(value) {
      state = 3;
      return { value, done: true };
    }
  };
}

// 测试
const gen1 = simpleGenerator();
const gen2 = simpleGeneratorStateMachine();

console.log(gen1.next()); // { value: 1, done: false }
console.log(gen2.next()); // { value: 1, done: false }

4.5 复杂 Generator 的状态机转换

/**
 * 包含变量和控制流的 Generator 状态机
 */

// 原始代码
function* complexGenerator(x) {
  const a = yield x + 1;
  const b = yield a + 2;
  return b + 3;
}

// 状态机实现
function complexGeneratorStateMachine(x) {
  let state = 0;
  let a, b;
  
  return {
    next(value) {
      switch (state) {
        case 0:
          state = 1;
          return { value: x + 1, done: false };
          
        case 1:
          a = value; // 接收上次 yield 的输入
          state = 2;
          return { value: a + 2, done: false };
          
        case 2:
          b = value;
          state = 3;
          return { value: b + 3, done: true };
          
        default:
          return { value: undefined, done: true };
      }
    }
  };
}

// 测试
const gen = complexGeneratorStateMachine(10);
console.log(gen.next());    // { value: 11, done: false }
console.log(gen.next(20));  // { value: 22, done: false }
console.log(gen.next(30));  // { value: 33, done: true }

常见面试题与最佳实践

5.1 经典面试题解析

题目1: async/await 执行顺序

async function async1() {
  console.log('async1 start');
  await async2();
  console.log('async1 end');
}

async function async2() {
  console.log('async2');
}

console.log('script start');

setTimeout(() => {
  console.log('setTimeout');
}, 0);

async1();

new Promise((resolve) => {
  console.log('promise1');
  resolve();
}).then(() => {
  console.log('promise2');
});

console.log('script end');

// 输出顺序:
// script start
// async1 start
// async2
// promise1
// script end
// async1 end
// promise2
// setTimeout

详细分析:

1. 同步代码: "script start"
2. setTimeout 进入宏任务队列
3. 执行 async1():
   - 输出 "async1 start"
   - 调用 async2(): 输出 "async2"
   - await 暂停,后续代码进入微任务队列
4. 执行 Promise 构造函数: "promise1"
5. then 进入微任务队列
6. 同步代码: "script end"
7. 执行微任务队列:
   - async1 恢复: "async1 end"
   - Promise then: "promise2"
8. 执行宏任务队列: "setTimeout"

题目2: 多个 await 的执行时机

async function test() {
  console.log('1');
  await console.log('2');
  console.log('3');
}

test();
console.log('4');

// 输出: 1, 2, 4, 3

// 为什么?
// await console.log('2') 会立即执行 console.log('2')
// 但 await 会暂停后续代码,将 console.log('3') 放入微任务

题目3: await 后面是非 Promise 值

async function test() {
  const a = await 1; // 相当于 await Promise.resolve(1)
  console.log(a);
  
  const b = await {
    then(resolve) {
      resolve(2);
    }
  };
  console.log(b);
}

test(); // 输出: 1, 2

题目4: 并发与串行的区别

// 串行执行(耗时 3秒)
async function serial() {
  const result1 = await request1(); // 1秒
  const result2 = await request2(); // 1秒
  const result3 = await request3(); // 1秒
  return [result1, result2, result3];
}

// 并发执行(耗时 1秒)
async function parallel() {
  const [result1, result2, result3] = await Promise.all([
    request1(),
    request2(),
    request3()
  ]);
  return [result1, result2, result3];
}

// 或者
async function parallel2() {
  const promise1 = request1(); // 立即发起请求
  const promise2 = request2(); // 立即发起请求
  const promise3 = request3(); // 立即发起请求
  
  const result1 = await promise1;
  const result2 = await promise2;
  const result3 = await promise3;
  
  return [result1, result2, result3];
}

5.2 常见陷阱与解决方案

陷阱1: forEach 中使用 await

// ❌ 错误:forEach 不会等待
async function processArray(array) {
  array.forEach(async (item) => {
    await asyncOperation(item);
  });
  console.log('Done'); // 会立即执行,不等待
}

// ✅ 正确方法1: for...of
async function processArray(array) {
  for (const item of array) {
    await asyncOperation(item);
  }
  console.log('Done'); // 等待所有操作完成
}

// ✅ 正确方法2: Promise.all(并发执行)
async function processArray(array) {
  await Promise.all(array.map(item => asyncOperation(item)));
  console.log('Done');
}

// ✅ 正确方法3: reduce(串行执行)
async function processArray(array) {
  await array.reduce(async (promise, item) => {
    await promise;
    await asyncOperation(item);
  }, Promise.resolve());
  console.log('Done');
}

陷阱2: try-catch 只能捕获直接 await 的错误

// ❌ 无法捕获错误
async function wrong() {
  try {
    const promise = asyncOperation(); // 未 await
    // ... 其他代码
    return promise; // 错误会被传递到外部
  } catch (error) {
    console.log('捕获不到');
  }
}

// ✅ 正确捕获
async function correct() {
  try {
    const result = await asyncOperation(); // 直接 await
    return result;
  } catch (error) {
    console.log('成功捕获:', error);
  }
}

陷阱3: 返回值的差异

// 情况1: 返回 Promise
async function case1() {
  return Promise.resolve(1); // 返回 Promise<1>
}

// 情况2: await 后返回
async function case2() {
  return await Promise.resolve(1); // 返回 Promise<1>
}

// 情况3: 直接返回值
async function case3() {
  return 1; // 返回 Promise<1>
}

// 看起来相同,但性能有差异
// case2 会多一次 Promise 展开,效率略低

5.3 性能优化技巧

技巧1: 避免不必要的 await

// ❌ 不必要的 await
async function bad() {
  return await fetch('/api/data'); // 多余的 await
}

// ✅ 直接返回 Promise
async function good() {
  return fetch('/api/data');
}

// 例外:需要 try-catch 时必须使用 await
async function needAwait() {
  try {
    return await fetch('/api/data'); // 必要的 await
  } catch (error) {
    console.error(error);
    return null;
  }
}

技巧2: 合理使用并发

// 场景:获取用户信息和订单,它们互不依赖

// ❌ 串行执行(慢)
async function getDataSerial(userId) {
  const user = await fetchUser(userId);      // 等待 1s
  const orders = await fetchOrders(userId);  // 再等待 1s
  return { user, orders };                   // 总共 2s
}

// ✅ 并发执行(快)
async function getDataParallel(userId) {
  const [user, orders] = await Promise.all([
    fetchUser(userId),
    fetchOrders(userId)
  ]);
  return { user, orders }; // 总共 1s
}

// ✅ 部分并发(有依赖关系时)
async function getDataMixed(userId) {
  const user = await fetchUser(userId);
  
  // 这两个依赖 user,但彼此独立
  const [orders, preferences] = await Promise.all([
    fetchOrders(user.id),
    fetchPreferences(user.id)
  ]);
  
  return { user, orders, preferences };
}

技巧3: 使用 Promise.allSettled 处理部分失败

// 场景:批量请求,部分失败不影响其他

// ❌ Promise.all 一个失败全部失败
async function bad(urls) {
  try {
    const results = await Promise.all(urls.map(url => fetch(url)));
    return results;
  } catch (error) {
    // 一个失败,所有结果丢失
    console.error('请求失败');
    return [];
  }
}

// ✅ Promise.allSettled 获取所有结果
async function good(urls) {
  const results = await Promise.allSettled(urls.map(url => fetch(url)));
  
  const successful = results
    .filter(r => r.status === 'fulfilled')
    .map(r => r.value);
    
  const failed = results
    .filter(r => r.status === 'rejected')
    .map(r => r.reason);
  
  console.log(`成功 ${successful.length} 个,失败 ${failed.length} 个`);
  return successful;
}

5.4 高级应用模式

模式1: 异步队列

class AsyncQueue {
  constructor(concurrency = 1) {
    this.concurrency = concurrency;
    this.running = 0;
    this.queue = [];
  }
  
  async add(asyncFunc) {
    while (this.running >= this.concurrency) {
      await new Promise(resolve => this.queue.push(resolve));
    }
    
    this.running++;
    
    try {
      return await asyncFunc();
    } finally {
      this.running--;
      const resolve = this.queue.shift();
      if (resolve) resolve();
    }
  }
}

// 使用
const queue = new AsyncQueue(2); // 最多2个并发

async function fetchData(id) {
  return queue.add(async () => {
    const data = await fetch(`/api/data/${id}`);
    return data;
  });
}

// 10个请求,但同时只有2个在执行
const results = await Promise.all(
  [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(id => fetchData(id))
);

模式2: 重试机制

async function retry(asyncFunc, maxRetries = 3, delay = 1000) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await asyncFunc();
    } catch (error) {
      if (i === maxRetries - 1) throw error;
      
      console.log(`第 ${i + 1} 次重试失败,${delay}ms 后重试`);
      await new Promise(resolve => setTimeout(resolve, delay));
      
      // 指数退避
      delay *= 2;
    }
  }
}

// 使用
const data = await retry(() => fetch('/api/unstable'), 5);

模式3: 超时控制

function timeout(promise, ms) {
  return Promise.race([
    promise,
    new Promise((_, reject) => 
      setTimeout(() => reject(new Error('超时')), ms)
    )
  ]);
}

// 使用
async function fetchWithTimeout() {
  try {
    const data = await timeout(fetch('/api/slow'), 5000);
    return data;
  } catch (error) {
    console.error('请求超时或失败:', error);
    return null;
  }
}

模式4: 取消 Promise

class CancelablePromise {
  constructor(executor) {
    this.isCanceled = false;
    
    this.promise = new Promise((resolve, reject) => {
      executor(
        value => {
          if (!this.isCanceled) resolve(value);
        },
        error => {
          if (!this.isCanceled) reject(error);
        }
      );
    });
  }
  
  cancel() {
    this.isCanceled = true;
  }
}

// 使用
const cancelable = new CancelablePromise((resolve, reject) => {
  setTimeout(() => resolve('完成'), 5000);
});

setTimeout(() => {
  cancelable.cancel();
  console.log('操作已取消');
}, 2000);

cancelable.promise.then(
  result => console.log(result),
  error => console.error(error)
);

实战应用场景

6.1 数据请求场景

场景1: 分页加载

class PaginationLoader {
  constructor(fetchFunc) {
    this.fetchFunc = fetchFunc;
    this.page = 1;
    this.hasMore = true;
    this.loading = false;
  }
  
  async loadMore() {
    if (!this.hasMore || this.loading) return;
    
    this.loading = true;
    
    try {
      const data = await this.fetchFunc(this.page);
      
      if (data.length === 0) {
        this.hasMore = false;
      } else {
        this.page++;
      }
      
      return data;
    } finally {
      this.loading = false;
    }
  }
  
  async loadAll() {
    const allData = [];
    
    while (this.hasMore) {
      const data = await this.loadMore();
      if (data) allData.push(...data);
    }
    
    return allData;
  }
}

// 使用
const loader = new PaginationLoader(async (page) => {
  const response = await fetch(`/api/items?page=${page}`);
  return response.json();
});

const allItems = await loader.loadAll();

场景2: 依赖请求链

async function fetchUserFullData(userId) {
  // 1. 获取用户基本信息
  const user = await fetchUser(userId);
  
  // 2. 基于用户信息,并发获取相关数据
  const [profile, orders, preferences] = await Promise.all([
    fetchUserProfile(user.profileId),
    fetchUserOrders(user.id),
    fetchUserPreferences(user.id)
  ]);
  
  // 3. 基于订单,获取订单详情(串行)
  const orderDetails = [];
  for (const order of orders.slice(0, 5)) { // 只获取前5个
    const detail = await fetchOrderDetail(order.id);
    orderDetails.push(detail);
  }
  
  return {
    user,
    profile,
    orders,
    orderDetails,
    preferences
  };
}

6.2 文件处理场景

class FileUploader {
  constructor(options = {}) {
    this.chunkSize = options.chunkSize || 1024 * 1024; // 1MB
    this.concurrency = options.concurrency || 3;
  }
  
  async uploadFile(file) {
    const chunks = this.splitFile(file);
    const uploadedChunks = [];
    
    // 分片并发上传
    for (let i = 0; i < chunks.length; i += this.concurrency) {
      const batch = chunks.slice(i, i + this.concurrency);
      
      const results = await Promise.all(
        batch.map((chunk, index) => 
          this.uploadChunk(chunk, i + index, chunks.length)
        )
      );
      
      uploadedChunks.push(...results);
    }
    
    // 合并分片
    return await this.mergeChunks(file.name, uploadedChunks);
  }
  
  splitFile(file) {
    const chunks = [];
    let start = 0;
    
    while (start < file.size) {
      const end = Math.min(start + this.chunkSize, file.size);
      chunks.push(file.slice(start, end));
      start = end;
    }
    
    return chunks;
  }
  
  async uploadChunk(chunk, index, total) {
    const formData = new FormData();
    formData.append('chunk', chunk);
    formData.append('index', index);
    formData.append('total', total);
    
    const response = await fetch('/api/upload/chunk', {
      method: 'POST',
      body: formData
    });
    
    return response.json();
  }
  
  async mergeChunks(filename, chunks) {
    const response = await fetch('/api/upload/merge', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ filename, chunks })
    });
    
    return response.json();
  }
}

// 使用
const uploader = new FileUploader({ concurrency: 5 });
const result = await uploader.uploadFile(file);

6.3 状态管理场景

class AsyncStateManager {
  constructor() {
    this.state = {};
    this.listeners = new Map();
    this.loadingStates = new Map();
  }
  
  async fetchAndUpdate(key, fetchFunc) {
    if (this.loadingStates.get(key)) {
      // 防止重复请求
      return this.loadingStates.get(key);
    }
    
    const promise = (async () => {
      try {
        const data = await fetchFunc();
        this.setState(key, data);
        return data;
      } finally {
        this.loadingStates.delete(key);
      }
    })();
    
    this.loadingStates.set(key, promise);
    return promise;
  }
  
  setState(key, value) {
    this.state[key] = value;
    this.notify(key, value);
  }
  
  subscribe(key, callback) {
    if (!this.listeners.has(key)) {
      this.listeners.set(key, new Set());
    }
    this.listeners.get(key).add(callback);
    
    // 返回取消订阅函数
    return () => {
      this.listeners.get(key).delete(callback);
    };
  }
  
  notify(key, value) {
    const callbacks = this.listeners.get(key);
    if (callbacks) {
      callbacks.forEach(cb => cb(value));
    }
  }
}

// 使用
const store = new AsyncStateManager();

// 订阅状态变化
const unsubscribe = store.subscribe('user', (user) => {
  console.log('用户更新:', user);
});

// 获取数据
await store.fetchAndUpdate('user', () => fetch('/api/user').then(r => r.json()));

6.4 React Hooks 集成

// 自定义异步 Hook
function useAsync(asyncFunc, dependencies = []) {
  const [state, setState] = React.useState({
    loading: false,
    data: null,
    error: null
  });
  
  React.useEffect(() => {
    let canceled = false;
    
    (async () => {
      setState({ loading: true, data: null, error: null });
      
      try {
        const data = await asyncFunc();
        if (!canceled) {
          setState({ loading: false, data, error: null });
        }
      } catch (error) {
        if (!canceled) {
          setState({ loading: false, data: null, error });
        }
      }
    })();
    
    return () => {
      canceled = true;
    };
  }, dependencies);
  
  return state;
}

// 使用
function UserProfile({ userId }) {
  const { loading, data: user, error } = useAsync(
    () => fetch(`/api/users/${userId}`).then(r => r.json()),
    [userId]
  );
  
  if (loading) return <div>加载中...</div>;
  if (error) return <div>错误: {error.message}</div>;
  if (!user) return null;
  
  return <div>{user.name}</div>;
}

6.5 错误恢复与降级

class ResilientFetcher {
  constructor(options = {}) {
    this.retries = options.retries || 3;
    this.timeout = options.timeout || 5000;
    this.fallback = options.fallback;
  }
  
  async fetch(url, options = {}) {
    // 尝试主请求
    try {
      return await this.fetchWithRetry(url, options);
    } catch (primaryError) {
      console.warn('主请求失败:', primaryError);
      
      // 尝试降级方案
      if (this.fallback) {
        try {
          console.log('尝试降级方案');
          return await this.fallback(url, options);
        } catch (fallbackError) {
          console.error('降级方案也失败:', fallbackError);
        }
      }
      
      throw primaryError;
    }
  }
  
  async fetchWithRetry(url, options) {
    let lastError;
    
    for (let i = 0; i < this.retries; i++) {
      try {
        return await this.fetchWithTimeout(url, options);
      } catch (error) {
        lastError = error;
        if (i < this.retries - 1) {
          await this.delay(Math.pow(2, i) * 1000);
        }
      }
    }
    
    throw lastError;
  }
  
  async fetchWithTimeout(url, options) {
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), this.timeout);
    
    try {
      const response = await fetch(url, {
        ...options,
        signal: controller.signal
      });
      
      if (!response.ok) {
        throw new Error(`HTTP ${response.status}`);
      }
      
      return await response.json();
    } finally {
      clearTimeout(timeoutId);
    }
  }
  
  delay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
}

// 使用
const fetcher = new ResilientFetcher({
  retries: 3,
  timeout: 5000,
  fallback: async (url) => {
    // 从缓存或备用服务器获取
    return await fetchFromCache(url);
  }
});

const data = await fetcher.fetch('/api/critical-data');

总结

核心要点

  1. Generator 本质

    • 可暂停、可恢复的函数
    • 基于状态机实现
    • 通过 yield 实现双向通信
  2. async/await 本质

    • Generator + Promise 的语法糖
    • 自动执行的 Generator
    • 更优雅的异步编程方式
  3. 执行机制

    • await 会暂停函数执行,将后续代码放入微任务队列
    • async 函数总是返回 Promise
    • 错误通过 try-catch 或 Promise.catch 捕获
  4. 性能优化

    • 合理使用并发(Promise.all)
    • 避免不必要的 await
    • 使用 Promise.allSettled 处理部分失败
  5. 最佳实践

    • 不要在 forEach 中使用 await
    • 使用 for…of 进行串行异步操作
    • 合理设计错误处理策略
    • 注意内存泄漏和取消操作

面试高频考点

  • async/await 与 Promise 的关系
  • Generator 执行机制和状态机原理
  • 事件循环中 async/await 的执行顺序
  • 并发控制和错误处理
  • 手写 async/await 实现(基于 Generator)

进阶学习方向

  • 深入理解事件循环机制
  • 学习 RxJS 等响应式编程库
  • 掌握 Web Worker 和多线程
  • 了解 TC39 提案中的新异步特性
  • 研究各框架的异步状态管理方案

参考资源: