前言

在 JavaScript 异步编程发展历程中,从最早期回调函数引发的回调地狱,再到Promise链式调用优化异步代码,虽然解决了多层嵌套痛点,但满屏的.then()依旧让代码可读性大打折扣。

async/await作为 ES7 推出的异步语法糖,基于 Promise 和生成器实现,彻底让异步代码拥有同步代码的书写风格,成为目前前端项目中最主流的异步写法。今天全方位拆解async/await核心知识点、执行机制、易错点以及实战选型技巧。

一、核心概念通俗理解

1. 关键字释义

  • async:异步标识,修饰在函数前面,声明该函数为异步函数特点:异步函数执行不会阻塞主线程后续同步代码,且隐式返回 Promise 对象
  • await:直译async wait,意为异步等待特点:只能写在 async 函数内部,暂停当前 async 函数内部代码执行,等待异步操作完成

2. 诞生原因

Promise 解决了回调地狱,但大量链式.then()堆砌,代码语义混乱、纵向嵌套冗长,逻辑阅读难度高。async/await应运而生,简化 Promise 调用,以同步思维编写异步逻辑,大幅提升代码简洁度与可读性。

3. 底层依托

async/await并非全新语法,底层依托两大技术实现:

  1. Promise:承接异步状态、成功 / 失败结果
  2. Generator 生成器:实现代码暂停、恢复执行的协程能力

二、深入解析 async 异步函数

1. 基础语法

只需在普通函数前添加async关键字,即可定义异步函数

// 定义异步函数
async function sayHello() {
  return "Hello async/await";
}

2. 核心特性:隐式返回 Promise

正常返回值:async 函数内部普通返回值,自动被Promise.resolve()包裹

async function fn() {
  return 100;
}
// 等价于普通Promise写法
function fn2() {
  return Promise.resolve(100);
}
console.log(fn()); // Promise {<fulfilled>: 100}

抛出错误:函数内部抛出异常,自动被Promise.reject()包裹

async function errFn() {
  throw new Error("异步执行失败");
}
console.log(errFn()); // Promise {<rejected>: Error}

3. 调用异步函数

async 函数调用方式和普通函数一致,想要获取最终结果,需通过.then()接收:

async function timeout() {
  return "异步执行完成";
}
// 调用并获取结果
timeout().then(res => console.log(res));
// 主线程同步代码优先执行
console.log("我优先执行,不受异步阻塞");

执行结论:async 函数内部逻辑异步执行,不会阻塞主线程同步代码运行。

三、深度掌握 await 等待机制

1. await 等待规则

  1. 核心限制await 只能存在于 async 函数中,普通函数直接使用直接报错
  2. 等待对象
    • 优先等待Promise 对象:阻塞当前 async 函数内部代码,等待 Promise 状态变为resolved/rejected,获取最终结果
    • 等待普通值 / 普通函数:V8 引擎自动封装为resolved状态的 Promise,直接返回值

2. 基础使用示例

// 模拟异步请求,返回Promise
function getMsg() {
  return new Promise(resolve => {
    setTimeout(() => resolve("后端数据"), 1000);
  });
}

// 使用async+await接收异步结果
async function getData() {
  // 等待异步执行完成,拿到结果赋值
  const res = await getMsg();
  console.log(res); // 1秒后打印:后端数据
}
getData();

3. 串行异步执行(经典场景)

多个异步操作存在先后依赖,使用 await 串行执行,写法极简:

// 2秒后数值翻倍
function doubleNum(num) {
  return new Promise(resolve => {
    setTimeout(() => resolve(num * 2), 2000);
  });
}

// 串行依次执行异步
async function calcTotal() {
  let n1 = await doubleNum(10);
  let n2 = await doubleNum(20);
  let n3 = await doubleNum(30);
  console.log(n1 + n2 + n3); // 6秒后输出 120
}
calcTotal();
console.log("主线程同步代码先行执行");

四、EventLoop 执行顺序(高频面试考点)

核心执行原则

  1. 遇到await仅阻塞当前 async 函数内部后续代码,不会阻塞全局主线程
  2. 主线程优先执行所有同步代码,同步执行完毕后,再执行微任务
  3. await 暂停的代码会进入微任务队列,宏任务最后执行

经典例题 1

async function fn1() {
  console.log(1);
  await fn2();
  console.log(2);
}
async function fn2() {
  console.log("fn2执行");
}
fn1();
console.log(3);
// 输出顺序:1 → fn2执行 → 3 → 2

经典例题 2

console.log(0);
async function test() {
  console.log(100);
  let x = await 200;
  console.log(x);
  console.log(200);
}
test();
console.log(300);
// 输出顺序:0 → 100 → 300 → 200 → 200

五、错误捕获与异常处理

await 等待的 Promise 一旦状态变为rejected,会直接抛出运行异常,必须通过try/catch捕获异常

async function request() {
  try {
    // 异步请求报错
    const res = await Promise.reject("接口请求失败");
    console.log(res);
  } catch (error) {
    // 统一捕获异常
    console.log("错误信息:", error);
  }
}
request();

六、实战场景:async/await 与 Promise 选型

1. 单异步请求

简单单请求、需要单独捕获错误:优先使用 Promise.then/catch,写法更轻便

// Promise写法
getUserInfo()
.then(res => console.log(res))
.catch(err => console.log(err));

// async/await写法
async function getUser() {
  try {
    const res = await getUserInfo();
  } catch(e) {}
}

2. 多层嵌套异步(依赖执行)

优先使用 async/await,彻底告别嵌套地狱,逻辑清晰直观

// 老式回调嵌套
getUser(id, (res) => {
  getOrder(res.uid, (order) => {
    getPay(order.id)
  })
})

// async/await优雅写法
await getUser(id);
await getOrder(uid);
await getPay(orderId);

3. 无依赖并发异步(重点避坑)

错误写法(串行执行,耗时翻倍)

// 两个异步互不依赖,串行执行浪费时间
await req1();
await req2();

正确写法(并发执行,节省时间)借助Promise.all实现并发请求,所有异步执行完成后统一处理

// 并发执行,同时发起请求
await Promise.all([req1(), req2()]);
console.log("所有请求全部完成");

七、必记使用注意事项

  1. async函数可以不写await,依旧会返回 Promise 对象
  2. await 只暂停当前函数内部代码,全局主线程不受影响
  3. Promise 创建时不会生成微任务,只有调用resolve/reject才会推入微任务队列
  4. 批量异步优先Promise.all,杜绝无效串行等待
  5. 所有 await 异步操作,建议统一用try/catch做异常兜底

总结

  1. async定义异步函数,隐式返回 Promise,不阻塞主线程;
  2. await暂停异步函数内部代码,等待异步结果,仅可在 async 内使用;
  3. async/await 简化 Promise 链式调用,实现同步写法写异步逻辑
  4. 简单异步用 Promise,嵌套 / 流程化异步用 async/await,并发异步搭配Promise.all
  5. 异步异常统一通过try/catch捕获,避免程序崩溃。

Logo

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

更多推荐