015-async & await(语法糖)

Async/Await 详解(JavaScript)

async/await 是 ES2017 (ES8) 引入的异步编程语法糖,基于 Promise 实现,让异步代码的书写和阅读更接近同步方式,彻底解决了回调地狱问题。


核心概念

  1. async 函数

    • 声明一个异步函数:async function myFunc() { ... }
    • 返回值:总是返回一个 Promise 对象。
      • 函数内 return 的值会被包装成 Promise.resolve(value)
      • 抛出错误时返回 Promise.reject(error)
    1
    2
    3
    4
    async function foo() {
    return 42; // 等价于 Promise.resolve(42)
    }
    foo().then(console.log); // 输出 42
  2. await 表达式

    • 只能在 async 函数内部使用。
    • await promise:暂停当前 async 函数的执行,等待 Promise 完成。
      • 若 Promise 成功(fulfilled),返回 resolve 的值
      • 若 Promise 失败(rejected),抛出错误(可用 try/catch 捕获)。
    1
    2
    3
    4
    5
    6
    7
    async function bar() {
    const result = await new Promise((resolve) =>
    setTimeout(() => resolve("完成!"), 1000)
    );
    console.log(result); // 1秒后输出 "完成!"
    }
    bar();

执行流程详解

1
2
3
4
5
6
7
8
9
10
async function example() {
console.log("1: 开始");
const data = await fetchData(); // 暂停,等待fetchData完成
console.log(`3: 获取数据: ${data}`);
return "处理完毕";
}

console.log("0: 调用前");
example().then(res => console.log(`4: 结果: ${res}`));
console.log("2: 调用后");

输出顺序

0: 调用前
1: 开始
2: 调用后    // 主线程继续执行
3: 获取数据: ... // 异步恢复执行
4: 结果: 处理完毕

错误处理

使用 try/catch 捕获异常:

1
2
3
4
5
6
7
8
9
async function fetchWithError() {
try {
const data = await fetch("https://invalid-url");
console.log(data);
} catch (error) {
console.error("请求失败:", error); // 捕获网络错误或reject
}
}
fetchWithError();

关键优势

  1. 代码简洁:消除嵌套回调,线性化异步逻辑。
  2. 错误处理统一:使用 try/catch 替代 .catch()
  3. 调试友好:可以在 await 行设置断点调试。
  4. 条件分支清晰if/for 等逻辑可直接使用。

注意事项

  1. 顶层 await
    ES2022 支持在模块顶层直接使用 await(仅限 ES Modules):

    1
    2
    // 模块中
    const data = await fetchData(); // 合法
  2. 并行优化
    多个独立异步操作应并行执行,避免串行等待:

    1
    2
    3
    4
    5
    6
    // 错误(串行): 耗时 time1 + time2
    const r1 = await task1();
    const r2 = await task2();

    // 正确(并行): 耗时 max(time1, time2)
    const [r1, r2] = await Promise.all([task1(), task2()]);
  3. 循环中的 await
    在循环中需注意执行顺序:

    1
    2
    3
    4
    5
    6
    7
    // 顺序执行(串行)
    for (const url of urls) {
    await fetch(url);
    }

    // 并行执行
    await Promise.all(urls.map(url => fetch(url)));

常见问题

Q: await 会阻塞主线程吗?
A: 不会await 只是暂停当前 async 函数的执行,将控制权交还事件循环,主线程可继续处理其他任务。

Q: 能否在普通函数中用 await
A: 不能!会抛出语法错误。只能在 async 函数内使用。

Q: async/await 会替代 Promise 吗?
A: 不会!它们是互补的:

  • async/await 用于控制异步流程
  • Promise 仍是异步操作的底层基础

总结

async/await 是 JavaScript 异步编程的革命性改进:

  1. 用同步写法实现异步逻辑
  2. 基于 Promise,兼容现有异步库
  3. 错误处理更符合直觉
  4. 大幅提升代码可读性和可维护性

最佳实践:所有返回 Promise 的函数都可用 await 调用,配合 try/catch 处理错误,用 Promise.all() 优化并行任务。