上一篇我们掌握了 Promise,它彻底解决了回调地狱的问题,让异步代码可以链式书写,变得清晰可维护。但链式调用如果层级过多,依然会有一定的冗余感。

ES7 推出的 async/await,是 Promise 的 “语法糖”—— 它没有新增任何底层逻辑,完全基于 Promise 实现,却能让异步代码写得和同步代码一模一样,可读性、可维护性再上一个台阶,也是现在实际开发中最常用的异步写法。

本文全程贴合前 5 篇风格,无多余格式,直接适配聊天窗口查看,重点讲解 async/await 的基本用法、核心特性、错误处理,以及和 Promise 的关联,所有代码可直接复制运行,兼顾入门和面试。


一、先搞懂:async/await 是什么?

一句话总结:async/await 是基于 Promise 的语法糖,目的是简化 Promise 的链式调用,让异步代码的写法更接近同步代码

核心关系:

  • async 用来修饰函数,表明这个函数是异步函数,函数内部可以使用 await
  • await 只能用在 async 函数内部,用来 “等待” 一个 Promise 完成(成功 / 失败),并返回 Promise 的结果;
  • 本质:await 就是 “等待” Promise 的 then 结果,async 函数的返回值,默认会被包装成一个成功的 Promise。

对比感受(同样的异步逻辑):

Promise 链式写法(上一篇)

javascript

运行

// 模拟接口请求
function requestData() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve("接口返回的数据");
    }, 1000);
  });
}

// Promise 链式调用
requestData()
  .then(res => {
    console.log("获取到数据:", res);
    return "处理后的数据";
  })
  .then(processedRes => {
    console.log("处理完成:", processedRes);
  });

async/await 写法(更简洁)

javascript

运行

// 模拟接口请求(和上面一致,还是返回 Promise)
function requestData() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve("接口返回的数据");
    }, 1000);
  });
}

// async 修饰异步函数
async function getData() {
  // await 等待 Promise 完成,直接拿到 resolve 的结果
  const res = await requestData();
  console.log("获取到数据:", res);
  const processedRes = "处理后的数据";
  console.log("处理完成:", processedRes);
  return processedRes; // 异步函数的返回值,会被包装成 Promise
}

// 调用异步函数(和调用 Promise 一样,可加 then)
getData().then(res => console.log("异步函数返回:", res));

可以看到:async/await 去掉了繁琐的 then 链式,代码自上而下执行,和同步代码的逻辑完全一致,一眼就能看懂。


二、async/await 基本用法(必掌握)

1. 基础语法:async 修饰函数

  • 函数前面加 async,函数就变成异步函数;
  • 异步函数内部可以使用 await,外部调用异步函数,会返回一个 Promise;
  • 即使异步函数没有 return,也会返回 Promise.resolve(undefined)

javascript

运行

// 1. 普通 async 函数(无 return)
async function fn1() {
  console.log("异步函数执行");
}
fn1(); // 执行函数,返回 Promise.resolve(undefined)
fn1().then(() => console.log("异步函数执行完成"));

// 2. 有 return 的 async 函数
async function fn2() {
  return "我是返回值"; // 等价于 return Promise.resolve("我是返回值")
}
fn2().then(res => console.log(res)); // 输出:我是返回值

// 3. 箭头函数写法(常用)
const fn3 = async () => {
  return await requestData(); // 结合 await 使用
};

2. 核心用法:await 等待 Promise

  • await 后面必须跟一个 Promise 对象(如果不是,会自动包装成 Promise.resolve(值));
  • await 会 “暂停” 异步函数的执行,等 Promise 完成(fulfilled)后,继续执行后续代码,并返回 Promise 的 resolve 结果;
  • 注意:await 只是 “暂停当前 async 函数”,不会阻塞整个 JS 线程(因为底层还是 Promise,属于异步微任务)。

javascript

运行

// 模拟两个接口请求,有依赖关系(先请求1,再请求2)
function request1() {
  return new Promise(resolve => {
    setTimeout(() => resolve("请求1返回的数据"), 1000);
  });
}

function request2(data) {
  return new Promise(resolve => {
    setTimeout(() => resolve(`请求2接收的数据:${data}`), 1000);
  });
}

// async/await 写法(同步式逻辑,解决依赖)
async function getTwoData() {
  console.log("开始请求1");
  const data1 = await request1(); // 等待请求1完成
  console.log("请求1完成,开始请求2");
  const data2 = await request2(data1); // 用请求1的结果请求2
  console.log("请求2完成,最终数据:", data2);
  return data2;
}

getTwoData();
// 输出顺序(1秒后输出请求1完成,再1秒后输出请求2完成):
// 开始请求1
// 请求1完成,开始请求2
// 请求2完成,最终数据: 请求2接收的数据:请求1返回的数据

3. 无依赖异步请求:并行执行

如果多个异步请求之间没有依赖关系(可以同时发起),直接用多个 await 会导致 “串行执行”(浪费时间),此时可以结合 Promise.all() 实现并行,提升效率。

javascript

运行

// 三个无依赖的接口请求
function requestA() {
  return new Promise(resolve => setTimeout(() => resolve("A数据"), 1000));
}
function requestB() {
  return new Promise(resolve => setTimeout(() => resolve("B数据"), 1000));
}
function requestC() {
  return new Promise(resolve => setTimeout(() => resolve("C数据"), 1000));
}

// 错误写法:串行执行,总耗时 3 秒
async function getSerialData() {
  const a = await requestA();
  const b = await requestB();
  const c = await requestC();
  console.log(a, b, c); // 总耗时 ~3000ms
}

// 正确写法:并行执行,总耗时 1 秒(推荐)
async function getParallelData() {
  // 先同时发起所有请求,得到三个 Promise
  const promiseA = requestA();
  const promiseB = requestB();
  const promiseC = requestC();
  // 等待所有 Promise 完成(并行)
  const [a, b, c] = await Promise.all([promiseA, promiseB, promiseC]);
  console.log(a, b, c); // 总耗时 ~1000ms
}

getParallelData();

三、错误处理:try/catch(核心)

Promise 用 catch() 捕获错误,而 async/await 因为写法和同步一致,所以用 try/catch 语句 捕获错误,更符合同步代码的错误处理逻辑,也更简洁。

1. 基本错误处理(单个 await)

javascript

运行

function requestData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      // 模拟请求失败
      reject("接口请求失败,网络错误");
    }, 1000);
  });
}

async function getData() {
  try {
    // 可能出错的代码(await 等待的 Promise 可能 reject)
    const res = await requestData();
    console.log("获取数据:", res);
  } catch (err) {
    // 捕获所有错误(包括 Promise reject 和代码本身的错误)
    console.log("出错了:", err);
  }
}

getData(); // 输出:出错了:接口请求失败,网络错误

2. 多个 await 的错误处理(统一捕获)

如果一个 async 函数中有多个 await,只要有一个 Promise 失败,整个函数就会中断,错误会被 catch 统一捕获。

javascript

运行

async function getMultiData() {
  try {
    const data1 = await request1(); // 假设请求1成功
    const data2 = await request2(data1); // 假设请求2失败
    const data3 = await request3(data2); // 不会执行(因为请求2失败,函数中断)
    console.log(data3);
  } catch (err) {
    // 捕获请求2的错误
    console.log("出错了:", err);
  }
}

3. 单独捕获某个 await 的错误

如果希望某个 await 的错误不影响其他 await 执行,可以在该 await 外层单独加 try/catch。

javascript

运行

async function getMultiData() {
  try {
    const data1 = await request1();
    // 单独捕获 request2 的错误
    let data2;
    try {
      data2 = await request2(data1);
    } catch (err) {
      console.log("request2 出错:", err);
      data2 = "默认数据"; // 给默认值,避免后续代码报错
    }
    // 即使 request2 失败,request3 依然会执行
    const data3 = await request3(data2);
    console.log(data3);
  } catch (err) {
    console.log("其他错误:", err);
  }
}

四、async/await 与 Promise 的区别(面试常考)

表格

对比维度 Promise async/await
本质 异步对象,底层实现异步逻辑 语法糖,基于 Promise 实现,无新增底层
写法 链式调用(then/catch) 同步式写法(try/catch)
可读性 比回调函数好,链式过多仍显冗余 最优,和同步代码逻辑一致,易读易维护
错误处理 用 catch () 捕获,链式中需单独处理 用 try/catch 捕获,统一或单独处理均可
调试 链式调用调试相对麻烦 同步式写法,调试更简单(可逐行断点)

核心结论:async/await 不是替代 Promise,而是对 Promise 的优化。所有 async/await 能实现的功能,Promise 都能实现;但 Promise 能实现的(如并行请求),async/await 结合 Promise API(all/race)也能实现,且写法更简洁。


五、高频坑点与面试题

坑点 1:await 只能用在 async 函数中

javascript

运行

// 错误写法:await 用在普通函数中
function fn() {
  const res = await requestData(); // 报错:await is only valid in async functions
}

// 正确写法:函数加 async
async function fn() {
  const res = await requestData();
}

坑点 2:async 函数返回值一定是 Promise

javascript

运行

async function fn() {
  return "hello"; // 等价于 return Promise.resolve("hello")
}
console.log(fn()); // 输出:Promise {<fulfilled>: 'hello'},不是直接的 "hello"

// 想要拿到返回值,必须用 then 或 await
fn().then(res => console.log(res)); // hello

async function fn2() {
  const res = await fn();
  console.log(res); // hello
}

坑点 3:多个 await 串行执行,浪费时间

如前面 “并行执行” 的案例,无依赖的多个 await 不要依次写,会导致串行执行,需结合 Promise.all() 实现并行,提升效率。

面试题 1:async/await 是什么?它和 Promise 的关系是什么?

答:async/await 是 ES7 推出的、基于 Promise 的语法糖,核心作用是简化 Promise 的链式调用,让异步代码写法更接近同步代码。它没有新增异步底层逻辑,完全依赖 Promise 实现,async 函数的返回值会自动包装成 Promise,await 本质是等待 Promise 的 then 结果。

面试题 2:async/await 如何处理错误?

答:用 try/catch 语句处理错误。将需要等待的 await 代码放在 try 块中,一旦某个 await 对应的 Promise 被 reject,或者代码本身出现错误,就会进入 catch 块,捕获并处理错误。如果需要单独处理某个 await 的错误,可以在该 await 外层嵌套 try/catch。

面试题 3:await 后面跟的不是 Promise 会怎么样?

答:如果 await 后面跟的不是 Promise 对象,JS 会自动将其包装成一个成功的 Promise,即 Promise.resolve(值),await 会直接返回这个值。

javascript

运行

async function fn() {
  const res1 = await 123; // 等价于 await Promise.resolve(123)
  const res2 = await "hello"; // 等价于 await Promise.resolve("hello")
  console.log(res1, res2); // 123 hello
}

面试题 4:async/await 比 Promise 好在哪里?

答:1. 可读性更好,写法和同步代码一致,避免了 Promise 链式调用的冗余;2. 错误处理更直观,用 try/catch 统一处理,比 Promise 多个 catch 更简洁;3. 调试更方便,同步式写法可逐行断点,无需在 then 中调试。


六、总结(核心要点)

  1. async/await 是 Promise 的语法糖,基于 Promise 实现,无新增底层逻辑;
  2. async 修饰函数,表明是异步函数,返回值默认包装成 Promise;
  3. await 只能用在 async 函数内部,等待 Promise 完成,返回 resolve 结果;
  4. 错误处理用 try/catch,可统一捕获,也可单独捕获;
  5. 无依赖异步请求,用 Promise.all() + await 实现并行,提升效率;
  6. 实际开发中,async/await 是首选异步写法,结合 Promise API 可处理所有异步场景。

到这里,JS 异步编程的核心(回调函数 → Promise → async/await)就全部讲完了。这三者是递进关系,回调是基础,Promise 解决回调地狱,async/await 优化 Promise 写法,掌握这三者,就能轻松应对所有前端异步开发场景。

下一篇我们将进入 JS 数组的高级用法,讲解数组常用的高阶函数(map、filter、reduce 等),这些是前端开发中高频使用的工具,也是面试重点。

📌 所有代码可直接复制到浏览器控制台运行,动手实操,重点感受 async/await 同步式的异步写法,以及 try/catch 的错误处理逻辑。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐