实现async/await

手写实现 async/await 语法糖,理解异步编程的本质

问题

async/await 是 ES2017 引入的异步编程语法糖,它基于 Generator 函数和 Promise 实现。本题要求手写实现 async/await 的机制,理解其背后的执行原理。

async/await 的本质是:

  • async 函数返回一个 Promise
  • await 会暂停函数执行,等待 Promise 完成
  • 通过 Generator 函数的 yield 和迭代器实现暂停和恢复

解答

/**
 * 实现 async/await 的函数
 * @param {GeneratorFunction} generatorFunc - Generator 函数
 * @returns {Promise} 返回一个 Promise
 */
function asyncToGenerator(generatorFunc) {
  return function(...args) {
    // 创建 Generator 对象
    const generator = generatorFunc.apply(this, args);
    
    return new Promise((resolve, reject) => {
      /**
       * 递归执行 Generator
       * @param {string} key - 'next' 或 'throw'
       * @param {any} arg - 传递给 Generator 的参数
       */
      function step(key, arg) {
        let result;
        
        try {
          // 执行 Generator 的 next 或 throw 方法
          result = generator[key](arg);
        } catch (error) {
          // 如果执行出错,reject Promise
          return reject(error);
        }
        
        const { value, done } = result;
        
        if (done) {
          // Generator 执行完毕,resolve 最终结果
          return resolve(value);
        } else {
          // 将 value 包装成 Promise,继续递归执行
          return Promise.resolve(value).then(
            val => step('next', val),      // 成功时继续执行
            err => step('throw', err)      // 失败时抛出错误
          );
        }
      }
      
      // 开始执行
      step('next');
    });
  };
}

使用示例

// 示例1:基本使用
function* fetchUser() {
  console.log('开始获取用户信息');
  const user = yield Promise.resolve({ id: 1, name: 'Alice' });
  console.log('用户信息:', user);
  
  const orders = yield Promise.resolve(['订单1', '订单2']);
  console.log('订单列表:', orders);
  
  return { user, orders };
}

const asyncFetchUser = asyncToGenerator(fetchUser);

asyncFetchUser().then(result => {
  console.log('最终结果:', result);
});

// 输出:
// 开始获取用户信息
// 用户信息: { id: 1, name: 'Alice' }
// 订单列表: ['订单1', '订单2']
// 最终结果: { user: { id: 1, name: 'Alice' }, orders: ['订单1', '订单2'] }


// 示例2:错误处理
function* fetchWithError() {
  try {
    const data = yield Promise.reject(new Error('网络错误'));
    console.log('这行不会执行');
  } catch (error) {
    console.log('捕获到错误:', error.message);
    return '错误已处理';
  }
}

const asyncFetchWithError = asyncToGenerator(fetchWithError);

asyncFetchWithError().then(result => {
  console.log('结果:', result);
});

// 输出:
// 捕获到错误: 网络错误
// 结果: 错误已处理


// 示例3:模拟真实的 async/await
function* getData() {
  const response1 = yield fetch('https://api.example.com/data1');
  const data1 = yield response1.json();
  
  const response2 = yield fetch(`https://api.example.com/data2?id=${data1.id}`);
  const data2 = yield response2.json();
  
  return { data1, data2 };
}

const asyncGetData = asyncToGenerator(getData);

// 等价于:
// async function getData() {
//   const response1 = await fetch('https://api.example.com/data1');
//   const data1 = await response1.json();
//   
//   const response2 = await fetch(`https://api.example.com/data2?id=${data1.id}`);
//   const data2 = await response2.json();
//   
//   return { data1, data2 };
// }


// 示例4:传递参数
function* greet(name) {
  const greeting = yield Promise.resolve(`Hello, ${name}!`);
  const farewell = yield Promise.resolve(`Goodbye, ${name}!`);
  return `${greeting} ${farewell}`;
}

const asyncGreet = asyncToGenerator(greet);

asyncGreet('Bob').then(result => {
  console.log(result); // Hello, Bob! Goodbye, Bob!
});

关键点

  • Generator 函数:使用 Generator 函数的 yield 关键字实现函数执行的暂停和恢复

  • Promise 包装:将每个 yield 的值用 Promise.resolve() 包装,统一处理同步和异步值

  • 递归执行:通过递归调用 step 函数,不断执行 Generator 的 next() 方法,直到 donetrue

  • 错误处理:使用 try-catch 捕获同步错误,使用 Promise 的 catch 处理异步错误,通过 generator.throw() 将错误传回 Generator

  • 返回 Promise:整个函数返回一个 Promise,使其可以被 .then() 或其他 await 调用

  • 上下文绑定:使用 apply(this, args) 保持函数执行时的 this 上下文和参数传递

  • done 判断:当 Generator 执行完毕(done === true)时,resolve 最终的返回值