当前位置:首页 > 文章列表 > 文章 > 前端 > async/await让异步代码更简洁易读

async/await让异步代码更简洁易读

2025-08-06 13:15:32 0浏览 收藏

积累知识,胜过积蓄金银!毕竟在文章开发的过程中,会遇到各种各样的问题,往往都是一些细节知识点还没有掌握好而导致的,因此基础知识点的积累是很重要的。下面本文《async/await 简化异步代码,提升可读性与维护性》,就带大家讲解一下知识点,若是你对本文感兴趣,或者是想搞懂其中某个知识点,就请你继续往下看吧~

async/await 是 ES2017 引入的语法糖,核心作用是让异步代码写起来像同步代码,提升可读性和维护性;2. 使用场景包括网络请求、数据库操作、文件读写等需等待异步结果的场合;3. 注意错误必须用 try...catch 捕获,避免未处理的 Promise 拒绝;4. 多个不依赖的异步任务应使用 Promise.all() 并行执行,避免串行性能损耗;5. async 函数始终返回 Promise,可被 .then() 处理或在其他 async 函数中 await,完整支持 Promise 生态。

js 中 async/await 语法作用 js 中 async/await 语法的使用场景

async/await 在 JavaScript 中,主要是为了让异步代码的编写和阅读变得更像同步代码,极大地提升了处理 Promise 的体验,让原本复杂的异步流程变得直观且易于维护。它的核心作用就是将基于回调或 .then() 链式的异步操作,转化为一种看起来是阻塞式、顺序执行的风格,但实际上仍然是非阻塞的。至于使用场景,凡是涉及到需要等待某个异步操作结果才能进行下一步处理的地方,它都能派上用场,比如网络请求、文件读写或者数据库操作等等。

js 中 async/await 语法作用 js 中 async/await 语法的使用场景

解决方案

async/await 是 ES2017 引入的语法糖,构建在 Promise 之上。理解它,得从两个关键词说起:

首先是 async 关键字,它用来定义一个函数。任何被 async 修饰的函数,都会自动返回一个 Promise 对象。即便你函数内部没有显式返回 Promise,它也会帮你包一层。比如:

js 中 async/await 语法作用 js 中 async/await 语法的使用场景
async function greet() {
  return "Hello, world!";
}

greet().then(message => console.log(message)); // 输出: Hello, world!

这个 Promise 会在 async 函数执行完毕时被解决(resolve),其解决值就是函数返回的值。如果函数内部抛出错误,Promise 就会被拒绝(reject)。

接着是 await 关键字,它只能在 async 函数内部使用。await 的作用是暂停 async 函数的执行,直到它等待的 Promise 状态变为已解决(resolved)或已拒绝(rejected)。一旦 Promise 解决,await 就会返回 Promise 的解决值;如果 Promise 拒绝,await 就会抛出一个错误。

js 中 async/await 语法作用 js 中 async/await 语法的使用场景

比如,我们有个模拟异步操作的函数:

function fetchData() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve("Data fetched successfully!");
    }, 2000);
  });
}

async function processData() {
  console.log("Starting data fetch...");
  try {
    const data = await fetchData(); // 暂停这里,等待 fetchData 的 Promise 解决
    console.log(data); // 2秒后输出: Data fetched successfully!
    console.log("Data processing complete.");
  } catch (error) {
    console.error("Error fetching data:", error);
  }
}

processData();
console.log("This line executes immediately, not waiting for processData to finish.");

在这个例子里,processData 函数虽然看起来是顺序执行的,但在 await fetchData() 那一行,processData 会“暂停”,让出执行权,直到 fetchData 返回的 Promise 完成。这使得异步流程的控制变得异常直观,避免了层层嵌套的 .then() 回调。

async/await 如何让异步代码更易读和维护?

说实话,我个人觉得 async/await 简直是 JavaScript 异步编程的救星。在此之前,我们处理异步通常是回调函数或者 Promise 的 .then() 链。回调地狱(Callback Hell)就不用说了,那简直是噩梦。Promise .then() 链虽然好一些,但当逻辑复杂,比如需要多次依赖上一个 Promise 的结果,或者有条件分支时,链条就会变得很长,阅读起来还是有点费劲,尤其是错误处理,得在每个 .then() 后面加 .catch() 或者在链的末尾统一处理,有时候总感觉不够“自然”。

async/await 的出现,直接把这种“自然”的感觉带回来了。它让你的异步代码看起来就像是同步代码一样,从上到下,一步一步执行。这极大地提升了代码的可读性。你不再需要费力去追踪那些散落在各处的 .then() 回调,代码流清晰明了,一眼就能看出逻辑顺序。

更重要的是错误处理。在 async 函数内部,你可以直接使用我们熟悉的 try...catch 语句来捕获 await 表达式可能抛出的错误。这和处理同步代码的错误方式完全一致,大大降低了心智负担。比如,你有一个请求用户信息的 fetchUser 和请求用户订单的 fetchOrders 两个异步操作,并且 fetchOrders 依赖 fetchUser 的结果:

async function getUserAndOrders(userId) {
  try {
    const user = await fetchUser(userId); // 假设 fetchUser 返回 Promise
    console.log(`Fetched user: ${user.name}`);

    // 如果 fetchUser 失败,这里就不会执行
    const orders = await fetchOrders(user.id); // 假设 fetchOrders 依赖 user.id
    console.log(`Fetched orders for ${user.name}:`, orders);

    return { user, orders };
  } catch (error) {
    // 任何一个 await 失败,都会被这里捕获
    console.error("Failed to get user or orders:", error.message);
    // 可以在这里做一些错误恢复或者返回默认值
    throw new Error("Operation failed due to an internal error."); // 重新抛出错误
  }
}

// 调用示例
getUserAndOrders(123)
  .then(data => console.log("Success:", data))
  .catch(err => console.error("Outer catch:", err.message));

// 模拟函数
function fetchUser(id) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (id === 123) {
        resolve({ id: 123, name: "Alice" });
      } else {
        reject(new Error("User not found!"));
      }
    }, 500);
  });
}

function fetchOrders(userId) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (userId === 123) {
        resolve([{ orderId: "A001" }, { orderId: "A002" }]);
      } else {
        reject(new Error("No orders for this user!"));
      }
    }, 700);
  });
}

这种 try...catch 的模式,让异步代码的错误处理变得和同步代码一样直观,维护起来自然也更轻松。

async/await 在实际开发中有哪些常见的应用场景?

实际项目中,async/await 的身影几乎无处不在,只要你和“等待”打交道,它就特别好用。

最最常见的,那肯定是 API 网络请求了。无论是前端的 fetch API 还是 Node.js 后端的 axiosnode-fetch,它们返回的都是 Promise。用 async/await 来封装这些请求,代码简直清晰得不得了。比如:

async function fetchProductDetails(productId) {
  try {
    const response = await fetch(`/api/products/${productId}`);
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    const data = await response.json();
    return data;
  } catch (error) {
    console.error("Failed to fetch product details:", error);
    // 可以返回一个默认值或者重新抛出错误
    return null;
  }
}

// 调用
fetchProductDetails(456).then(product => {
  if (product) {
    console.log("Product:", product.name);
  }
});

其次,数据库操作也是 async/await 的重度使用者。无论你用的是 Mongoose(MongoDB),Sequelize(SQL),还是其他 ORM/ODM 库,它们的方法大多都返回 Promise。在 Node.js 后端服务中,处理用户请求、查询数据库、更新数据等一系列操作,用 async/await 串联起来,业务逻辑一目了然。

// 假设这是在 Node.js 后端,使用 Mongoose
async function createUserAndProfile(userData, profileData) {
  const session = await mongoose.startSession(); // 开启事务
  session.startTransaction();
  try {
    const newUser = await User.create([userData], { session });
    profileData.userId = newUser[0]._id; // 关联用户ID
    await Profile.create([profileData], { session });

    await session.commitTransaction(); // 提交事务
    console.log("User and profile created successfully.");
    return newUser[0];
  } catch (error) {
    await session.abortTransaction(); // 回滚事务
    console.error("Failed to create user or profile:", error);
    throw error;
  } finally {
    session.endSession();
  }
}

再来就是 文件系统操作 (Node.js)。Node.js 的 fs.promises API 提供了 Promise 版本的异步文件操作方法,结合 async/await,读写文件变得非常顺滑:

const fs = require('fs').promises;

async function readFileAndProcess(filePath) {
  try {
    const content = await fs.readFile(filePath, 'utf8');
    console.log("File content:", content.toUpperCase());
    await fs.writeFile(`${filePath}.bak`, content); // 写入备份
    console.log("Backup created.");
  } catch (error) {
    console.error("Error processing file:", error);
  }
}

readFileAndProcess('mydata.txt');

还有一些场景,比如 执行依赖于前一个异步结果的连续操作,或者 在浏览器环境中,等待一些特定事件或动画完成(通过将事件封装成 Promise)。总之,只要你的代码流程中包含“等待一个异步结果再继续”的逻辑,async/await 都是一个极佳的选择。

使用 async/await 时需要注意哪些“坑”或最佳实践?

虽然 async/await 带来了巨大的便利,但用起来还是有些地方需要留心,不然可能会踩到一些小“坑”,或者写出效率不高的代码。

首先,也是最重要的一点,是 错误处理。我见过不少人,觉得 async/await 看起来像同步代码,就忘了它本质还是 Promise,结果没有用 try...catch 包裹 await 调用。这样一来,如果 await 的 Promise 拒绝了,这个错误就会变成一个未捕获的 Promise 拒绝(Unhandled Promise Rejection),在 Node.js 环境下可能会导致进程崩溃,在浏览器里也可能只是在控制台打印个错误,但你无法优雅地处理它。所以,记住,只要有 await,就几乎总要考虑用 try...catch 包裹起来,或者确保在 async 函数的调用链末尾有一个 .catch()

// 错误示例:可能导致 Unhandled Promise Rejection
async function fetchDataUnsafe() {
  const data = await Promise.reject(new Error("Network error!"));
  console.log(data); // 这行不会执行
}
fetchDataUnsafe(); // 浏览器或 Node.js 会报告未捕获的拒绝

// 正确姿势:使用 try...catch
async function fetchDataSafe() {
  try {
    const data = await Promise.reject(new Error("Network error!"));
    console.log(data);
  } catch (error) {
    console.error("Caught error:", error.message);
  }
}
fetchDataSafe();

// 或者在调用 async 函数的地方用 .catch()
async function fetchDataSafeOuter() {
  const data = await Promise.reject(new Error("Network error!"));
  console.log(data);
}
fetchDataSafeOuter().catch(error => console.error("Caught error outside:", error.message));

第二个常见的误区是 并行执行await 会暂停当前 async 函数的执行,直到其等待的 Promise 解决。这意味着如果你有多个不相互依赖的异步操作,并且你用 await 一个接一个地去等待它们,那么它们就会串行执行,白白浪费了异步的并行能力。比如:

// 串行执行,效率较低
async function fetchAllDataSerial() {
  console.time("Serial Fetch");
  const users = await fetchUsers();     // 等待用户数据
  const products = await fetchProducts(); // 等待产品数据,即使它不依赖用户数据
  const orders = await fetchOrders();   // 等待订单数据
  console.timeEnd("Serial Fetch");
  return { users, products, orders };
}

// 推荐:使用 Promise.all() 或 Promise.allSettled() 进行并行执行
async function fetchAllDataParallel() {
  console.time("Parallel Fetch");
  // 同时发起所有请求,然后等待它们全部完成
  const [users, products, orders] = await Promise.all([
    fetchUsers(),
    fetchProducts(),
    fetchOrders()
  ]);
  console.timeEnd("Parallel Fetch");
  return { users, products, orders };
}

// 模拟异步函数
function fetchUsers() { return new Promise(r => setTimeout(() => r("Users Data"), 1000)); }
function fetchProducts() { return new Promise(r => setTimeout(() => r("Products Data"), 800)); }
function fetchOrders() { return new Promise(r => setTimeout(() => r("Orders Data"), 1200)); }

fetchAllDataSerial(); // 大约需要 1000 + 800 + 1200 = 3000ms
fetchAllDataParallel(); // 大约只需要 1200ms (取决于最慢的那个)

如果你需要等待所有 Promise 完成,即使其中有失败的,并且你想知道每个 Promise 的状态,可以使用 Promise.allSettled()

还有一点,顶层 await。在 ES 模块(ES Modules)中,现在可以直接在模块的顶层使用 await,而不需要将其包裹在 async 函数中。这在一些脚本或初始化逻辑中非常方便。但在非 ES 模块环境(比如 CommonJS 模块或老旧的浏览器脚本)中,你仍然需要将 await 放在 async 函数内部。

最后,记住 async 函数本身返回的是一个 Promise。这意味着当你调用一个 async 函数时,你得到的是一个 Promise,你仍然可以通过 .then().catch() 来处理它,或者在另一个 async 函数中 await 它。这是理解 async/await 如何与现有 Promise 生态系统融合的关键。

保持代码的清晰和可维护性,是 async/await 带来的最大价值。合理利用 try...catchPromise.all()/Promise.allSettled(),能让你写出既高效又健壮的异步代码。

本篇关于《async/await让异步代码更简洁易读》的介绍就到此结束啦,但是学无止境,想要了解学习更多关于文章的相关知识,请关注golang学习网公众号!

Go语言TCP读取EOF优化技巧Go语言TCP读取EOF优化技巧
上一篇
Go语言TCP读取EOF优化技巧
JavaScript闭包循环应用技巧
下一篇
JavaScript闭包循环应用技巧
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    542次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    511次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    498次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    484次学习
查看更多
AI推荐
  • 千音漫语:智能声音创作助手,AI配音、音视频翻译一站搞定!
    千音漫语
    千音漫语,北京熠声科技倾力打造的智能声音创作助手,提供AI配音、音视频翻译、语音识别、声音克隆等强大功能,助力有声书制作、视频创作、教育培训等领域,官网:https://qianyin123.com
    117次使用
  • MiniWork:智能高效AI工具平台,一站式工作学习效率解决方案
    MiniWork
    MiniWork是一款智能高效的AI工具平台,专为提升工作与学习效率而设计。整合文本处理、图像生成、营销策划及运营管理等多元AI工具,提供精准智能解决方案,让复杂工作简单高效。
    112次使用
  • NoCode (nocode.cn):零代码构建应用、网站、管理系统,降低开发门槛
    NoCode
    NoCode (nocode.cn)是领先的无代码开发平台,通过拖放、AI对话等简单操作,助您快速创建各类应用、网站与管理系统。无需编程知识,轻松实现个人生活、商业经营、企业管理多场景需求,大幅降低开发门槛,高效低成本。
    128次使用
  • 达医智影:阿里巴巴达摩院医疗AI影像早筛平台,CT一扫多筛癌症急慢病
    达医智影
    达医智影,阿里巴巴达摩院医疗AI创新力作。全球率先利用平扫CT实现“一扫多筛”,仅一次CT扫描即可高效识别多种癌症、急症及慢病,为疾病早期发现提供智能、精准的AI影像早筛解决方案。
    121次使用
  • 智慧芽Eureka:更懂技术创新的AI Agent平台,助力研发效率飞跃
    智慧芽Eureka
    智慧芽Eureka,专为技术创新打造的AI Agent平台。深度理解专利、研发、生物医药、材料、科创等复杂场景,通过专家级AI Agent精准执行任务,智能化工作流解放70%生产力,让您专注核心创新。
    126次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码