0

0

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

月夜之吻

月夜之吻

发布时间:2025-07-22 19:14:01

|

545人浏览过

|

来源于php中文网

原创

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 的身影几乎无处不在,只要你和“等待”打交道,它就特别好用。

皮卡智能
皮卡智能

AI驱动高效视觉设计平台

下载

最最常见的,那肯定是 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(),能让你写出既高效又健壮的异步代码。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
js获取数组长度的方法
js获取数组长度的方法

在js中,可以利用array对象的length属性来获取数组长度,该属性可设置或返回数组中元素的数目,只需要使用“array.length”语句即可返回表示数组对象的元素个数的数值,也就是长度值。php中文网还提供JavaScript数组的相关下载、相关课程等内容,供大家免费下载使用。

559

2023.06.20

js刷新当前页面
js刷新当前页面

js刷新当前页面的方法:1、reload方法,该方法强迫浏览器刷新当前页面,语法为“location.reload([bForceGet]) ”;2、replace方法,该方法通过指定URL替换当前缓存在历史里(客户端)的项目,因此当使用replace方法之后,不能通过“前进”和“后退”来访问已经被替换的URL,语法为“location.replace(URL) ”。php中文网为大家带来了js刷新当前页面的相关知识、以及相关文章等内容

438

2023.07.04

js四舍五入
js四舍五入

js四舍五入的方法:1、tofixed方法,可把 Number 四舍五入为指定小数位数的数字;2、round() 方法,可把一个数字舍入为最接近的整数。php中文网为大家带来了js四舍五入的相关知识、以及相关文章等内容

776

2023.07.04

js删除节点的方法
js删除节点的方法

js删除节点的方法有:1、removeChild()方法,用于从父节点中移除指定的子节点,它需要两个参数,第一个参数是要删除的子节点,第二个参数是父节点;2、parentNode.removeChild()方法,可以直接通过父节点调用来删除子节点;3、remove()方法,可以直接删除节点,而无需指定父节点;4、innerHTML属性,用于删除节点的内容。

481

2023.09.01

JavaScript转义字符
JavaScript转义字符

JavaScript中的转义字符是反斜杠和引号,可以在字符串中表示特殊字符或改变字符的含义。本专题为大家提供转义字符相关的文章、下载、课程内容,供大家免费下载体验。

574

2023.09.04

js生成随机数的方法
js生成随机数的方法

js生成随机数的方法有:1、使用random函数生成0-1之间的随机数;2、使用random函数和特定范围来生成随机整数;3、使用random函数和round函数生成0-99之间的随机整数;4、使用random函数和其他函数生成更复杂的随机数;5、使用random函数和其他函数生成范围内的随机小数;6、使用random函数和其他函数生成范围内的随机整数或小数。

1091

2023.09.04

如何启用JavaScript
如何启用JavaScript

JavaScript启用方法有内联脚本、内部脚本、外部脚本和异步加载。详细介绍:1、内联脚本是将JavaScript代码直接嵌入到HTML标签中;2、内部脚本是将JavaScript代码放置在HTML文件的`<script>`标签中;3、外部脚本是将JavaScript代码放置在一个独立的文件;4、外部脚本是将JavaScript代码放置在一个独立的文件。

659

2023.09.12

Js中Symbol类详解
Js中Symbol类详解

javascript中的Symbol数据类型是一种基本数据类型,用于表示独一无二的值。Symbol的特点:1、独一无二,每个Symbol值都是唯一的,不会与其他任何值相等;2、不可变性,Symbol值一旦创建,就不能修改或者重新赋值;3、隐藏性,Symbol值不会被隐式转换为其他类型;4、无法枚举,Symbol值作为对象的属性名时,默认是不可枚举的。

554

2023.09.20

Python 自然语言处理(NLP)基础与实战
Python 自然语言处理(NLP)基础与实战

本专题系统讲解 Python 在自然语言处理(NLP)领域的基础方法与实战应用,涵盖文本预处理(分词、去停用词)、词性标注、命名实体识别、关键词提取、情感分析,以及常用 NLP 库(NLTK、spaCy)的核心用法。通过真实文本案例,帮助学习者掌握 使用 Python 进行文本分析与语言数据处理的完整流程,适用于内容分析、舆情监测与智能文本应用场景。

2

2026.01.27

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
MongoDB 教程
MongoDB 教程

共17课时 | 2.4万人学习

黑马云课堂mongodb实操视频教程
黑马云课堂mongodb实操视频教程

共11课时 | 3.1万人学习

MongoDB 教程
MongoDB 教程

共42课时 | 27.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号