创见博客
Promise的语法糖async/await
七崽爱吃小饼干2026/01/29阅读 2专栏 JavaScript

JavaScript 中的 async/await 语法是处理异步操作的语法糖(底层还是基于 Promise 实现),核心作用是让异步代码写起来、读起来像同步代码一样直观,解决了传统 Promise 链式调用(.then())可能出现的“回调地狱”问题。

一、先搞懂前置知识:Promise

async/await 离不开 Promise:

  1. Promise 是处理异步操作的对象,有三种状态:pending(进行中)、fulfilled(成功完成)、rejected(失败)
  2. 异步操作成功用 resolve() 返回结果,失败用 reject() 返回错误
  3. 可以用 .then() 接收成功结果,.catch() 捕获错误

举个简单的 Promise 示例(模拟接口请求):

javascript
// 模拟一个异步请求:获取用户信息
const getUserInfo = () => {
  return new Promise((resolve, reject) => {
    // 用 setTimeout 模拟网络延迟(异步操作)
    setTimeout(() => {
      const success = true; // 模拟请求成功/失败
      if (success) {
        resolve({ name: "张三", age: 25 }); // 成功:返回用户信息
      } else {
        reject(new Error("获取用户信息失败")); // 失败:返回错误信息
      }
    }, 1000);
  });
};

// 调用 Promise(传统 .then() 方式)
getUserInfo()
  .then((user) => {
    console.log("用户信息:", user);
  })
  .catch((err) => {
    console.error("错误:", err);
  });

这种方式已经比回调函数好很多,但如果有多个依赖的异步操作(比如先获取用户信息,再根据用户 ID 获取订单),就会出现 .then() 嵌套,代码可读性下降,这时候 async/await 就派上用场了。

二、async 关键字

首先,async 关键字用于修饰函数,有两个核心作用:

  1. 被 async 修饰的函数,返回值一定是一个 Promise 对象(无论你函数内部 return 了什么,JS 都会自动把它包装成 Promise)
  2. 只有在 async 函数内部,才能使用 await 关键字(await 不能单独在普通函数中使用)

示例:async 函数的返回值

javascript
// 1. async 函数返回普通值
async function fn1() {
  return "hello async";
}

// 调用 fn1,返回的是 Promise 对象,可用 .then() 接收
fn1().then((res) => {
  console.log(res); // 输出:hello async
});

// 2. async 函数直接返回 Promise
async function fn2() {
  return new Promise((resolve) => {
    resolve("hello promise");
  });
}

fn2().then((res) => {
  console.log(res); // 输出:hello promise
});

三、await 关键字

await 顾名思义就是“等待”,它只能用在 async 函数内部,核心作用是: 等待一个 Promise 对象的状态变为 fulfilled(成功),并返回 Promise 内部 resolve() 的结果;如果 Promise 状态变为 rejected(失败),则会抛出错误,需要用 try/catch 捕获。

语法格式

javascript
async function 函数名() {
  try {
    // 等待 Promise 成功,并接收结果
    const 结果 = await 一个Promise对象;
  } catch (err) {
    // 捕获 Promise 失败的错误
    console.error(err);
  }
}

示例1:用 async/await 改写上面的 Promise 示例

javascript
// 模拟异步请求(和之前一样的 Promise 函数)
const getUserInfo = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const success = true;
      if (success) {
        resolve({ name: "张三", age: 25 });
      } else {
        reject(new Error("获取用户信息失败"));
      }
    }, 1000);
  });
};

// 用 async/await 调用异步操作
async function getResult() {
  try {
    // 等待 getUserInfo 的 Promise 完成,直接接收结果(像同步代码一样)
    const user = await getUserInfo();
    console.log("用户信息:", user); // 1秒后输出:用户信息: { name: '张三', age: 25 }
  } catch (err) {
    // 捕获异步操作的错误
    console.error("错误:", err);
  }
}

// 调用 async 函数
getResult();

示例2:多个依赖异步操作(无嵌套,更直观)

比如:先获取用户信息,再根据用户姓名获取用户订单,两个异步操作有依赖关系:

javascript
// 1. 模拟获取用户信息(Promise)
const getUserInfo = () => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({ name: "张三", age: 25 });
    }, 1000);
  });
};

// 2. 模拟根据用户名获取订单(Promise,依赖用户名)
const getUserOrder = (userName) => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve([
        { orderId: "001", goods: "手机" },
        { orderId: "002", goods: "电脑" }
      ]);
    }, 1000);
  });
};

// 3. 用 async/await 处理依赖异步操作
async function getuserAllData() {
  try {
    // 第一步:获取用户信息(等待1秒)
    const user = await getUserInfo();
    console.log("获取到用户:", user.name);

    // 第二步:根据用户名获取订单(等待1秒,依赖第一步的结果)
    const orders = await getUserOrder(user.name);
    console.log("获取到用户订单:", orders);
  } catch (err) {
    console.error("错误:", err);
  }
}

// 调用函数:总共耗时2秒,按顺序输出结果
getuserAllData();

如果用传统的 .then() 实现,会出现嵌套:

javascript
// .then() 链式调用(有嵌套,可读性不如 async/await)
getUserInfo()
  .then((user) => {
    console.log("获取到用户:", user.name);
    return getUserOrder(user.name); // 返回下一个 Promise
  })
  .then((orders) => {
    console.log("获取到用户订单:", orders);
  })
  .catch((err) => {
    console.error("错误:", err);
  });

对比之下,async/await 的代码结构更清晰,逻辑更易理解。

四、关键注意点

  1. await 只能等待 Promise:如果 await 后面不是 Promise 对象,它会直接返回该值(相当于同步执行)
    javascript
    async function fn() {
      const res = await "不是Promise";
      console.log(res); // 直接输出:不是Promise
    }
    fn();
    
  2. async/await 不会阻塞全局代码:await 只会阻塞当前 async 函数内部的代码执行(等待异步操作完成),不会影响函数外部的全局代码执行
    javascript
    async function fn() {
      console.log("开始执行异步操作");
      await new Promise((resolve) => setTimeout(resolve, 1000));
      console.log("异步操作完成(内部阻塞)");
    }
    
    // 调用 async 函数
    fn();
    // 全局代码不会被阻塞,会先于 await 内部的后续代码执行
    console.log("全局代码执行");
    
    // 输出顺序:
    // 开始执行异步操作
    // 全局代码执行
    // (1秒后)异步操作完成(内部阻塞)
    
  3. 错误处理的两种方式:
    • 方式1:try/catch(推荐,更贴合同步代码的错误处理逻辑,适合处理单个或少数异步操作)
    • 方式2:给 await 的 Promise 后面加 .catch()(适合多个独立异步操作的错误单独处理)
      javascript
      async function fn() {
        // 单个异步操作的错误捕获(方式2)
        const user = await getUserInfo().catch((err) => {
          console.error("获取用户失败:", err);
          return null; // 返回默认值,避免后续代码报错
        });
        if (user) {
          console.log(user);
        }
      }
      
  4. 并行执行多个独立异步操作:如果多个异步操作之间没有依赖关系,不要用串行的 await(会浪费时间),可以用 Promise.all() 实现并行执行
    javascript
    // 两个无依赖的异步操作
    const fnA = () => new Promise((resolve) => setTimeout(() => resolve("A完成"), 1000));
    const fnB = () => new Promise((resolve) => setTimeout(() => resolve("B完成"), 1000));
    
    // 错误:串行执行(总共耗时2秒)
    async function serialFn() {
      const resA = await fnA();
      const resB = await fnB();
      console.log(resA, resB);
    }
    
    // 正确:并行执行(总共耗时1秒)
    async function parallelFn() {
      // 先同时启动所有异步操作
      const promiseA = fnA();
      const promiseB = fnB();
      // 再等待所有结果返回
      const resA = await promiseA;
      const resB = await promiseB;
      // 或者直接用 Promise.all()
      // const [resA, resB] = await Promise.all([fnA(), fnB()]);
      console.log(resA, resB);
    }
    
    parallelFn();
    

总结

  1. async/await 是 Promise 的语法糖,用于简化异步代码的编写,让异步代码更接近同步逻辑。
  2. async 修饰函数,使其返回 Promise,且允许内部使用 await;await 等待 Promise 完成并返回结果,失败则抛出错误。
  3. 错误处理优先用 try/catch,无依赖异步操作用 Promise.all() 并行执行以提高效率。
评论
0/100