async await

76 阅读2分钟

一、核心概念:async/await 与 Promise 的关系

1. 本质

  • async/await 是 Promise 的语法糖,使异步代码看起来像同步代码。
  • async 函数返回一个 Promise,await 用于等待 Promise resolve/reject。

2. 对比传统异步写法

// Promise 写法  
fetchData().then(res => {  
  return processData(res);  
}).then(result => {  
  console.log(result);  
}).catch(err => {  
  console.error(err);  
});  

// async/await 写法(更简洁)  
async function fetchAndProcess() {  
  try {  
    const res = await fetchData();  
    const result = await processData(res);  
    console.log(result);  
  } catch (err) {  
    console.error(err);  
  }  
}  

二、async 函数的返回值

  • async 函数始终返回一个 Promise:
    async function fn() {  
      return 123;  
    }  
    
    fn().then(res => console.log(res)); // 输出: 123  
    
  • 若返回非 Promise 值,会自动包装为 Promise.resolve(值);若抛出错误,等价于 Promise.reject(错误)

三、await 的使用规则

1. 必须在 async 函数中使用

async function demo() {  
  const data = await fetch('api'); // ✅ 合法  
}  

2. 等待 Promise 或普通值

async function demo() {  
  const num = await 100; // num = 100(普通值会被包装为 resolved Promise)  
  const user = await fetchUser(); // 等待 Promise resolve  
}  

3. 错误处理

  • 使用 try/catch 捕获单个 await 的错误:
    async function demo() {  
      try {  
        const data = await fetchData();  
      } catch (err) {  
        console.error('获取数据失败', err);  
      }  
    }  
    
  • 多个 await 可共用一个 try/catch
    async function demo() {  
      try {  
        const res1 = await fetch1();  
        const res2 = await fetch2();  
      } catch (err) {  
        console.error('任一请求失败', err);  
      }  
    }  
    

四、并行异步操作:Promise.all vs await

1. 串行执行(逐个等待)

async function serial() {  
  const res1 = await fetch1();  
  const res2 = await fetch2(); // 等待 res1 完成后执行  
  return [res1, res2];  
}  

2. 并行执行(同时等待)

async function parallel() {  
  const p1 = fetch1();  
  const p2 = fetch2();  
  const [res1, res2] = await Promise.all([p1, p2]);  
  return [res1, res2];  
}  

优势:并行执行可减少总体耗时,适用于不依赖前置结果的请求。

五、async/await 中的错误冒泡

  • 未捕获的错误会向上抛出:
    async function fn1() {  
      await fn2(); // 若 fn2 抛出错误,会被 fn1 的 try/catch 捕获  
    }  
    
    async function demo() {  
      try {  
        await fn1();  
      } catch (err) {  
        console.error('全局错误捕获', err);  
      }  
    }  
    

六、问题

1. async/await 如何处理 Promise 拒绝

  • 使用 try/catch 捕获 await 后的错误:
    const data = await fetchData().catch(err => handleErr(err));  
    
  • 或在 async 函数中用 try/catch 包裹:
    const data = await (async () => {  
      try {  
        return await fetchData();  
      } catch (err) {  
        return defaultData;  
      }  
    })();  
    

2. 问:async/await 与 Generator 函数的区别?

  • async/await 是 ES7 标准,语法更简洁,自动处理 Promise。
  • Generator 需手动调用 next(),需配合 co 库使用(已被 async/await 替代)。

3. 问:如何中断正在执行的 async 函数?

  • 使用 AbortController(Web API):
    const controller = new AbortController();  
    async function fetchWithTimeout() {  
      setTimeout(() => controller.abort(), 5000); // 5秒后中断  
      try {  
        const res = await fetch('api', { signal: controller.signal });  
        return res;  
      } catch (err) {  
        if (err.name === 'AbortError') {  
          console.log('请求已取消');  
        }  
      }  
    }  
    

七、性能与注意事项

  1. 避免过度等待
    • 能并行的请求不要串行,用 Promise.all 优化效率。
  2. 错误边界
    • 重要业务逻辑建议每个 await 都包裹 try/catch,避免一个错误中断整个流程。
  3. 兼容性
    • 低版本浏览器需引入 regenerator-runtime 垫片。