题目

好的,我们进入异步编程的“终极形态”:async/await

async/await 是在 ES2017 (ES8) 中引入的,它并不是一个全新的功能,而是建立在 Promise 之上的语法糖 (Syntactic Sugar)。它的目标是让我们能够以一种看似同步、更符合人类直觉的方式来编写和阅读异步代码,从而彻底解决 Promise 链条过长的问题。


练习 05: async/await - 像写同步代码一样处理异步

在这个练习中,我们不会去修改上一关写的 fetchUserData 函数(因为它返回 Promise,已经很完美了)。我们的任务是改变调用它的方式,用 async/await 来替代 .then().catch()

🎯 学习目标:

  • 学会使用 async 关键字来声明一个异步函数。
  • 学会使用 await 关键字来“暂停”函数的执行,直到一个 Promise 完成,并获取其结果。
  • 理解 await 只能在 async 函数内部使用。
  • 学会使用标准的 try...catch 语句来捕获 await 期间可能发生的错误(即 Promise 的 reject)。

背景知识:

  • async function: 在一个函数声明前加上 async 关键字,这个函数就变成了一个异步函数。异步函数有一个重要特性:它总是隐式地返回一个 Promise。如果你的函数代码返回了一个具体的值(比如一个对象或字符串),async 函数会自动把它包装在一个 fulfilled 状态的 Promise 中返回。
  • await operator: await 关键字只能用在 async 函数内部。它可以“等待”一个 Promise 对象。当代码执行到 await somePromise 时,它会暂停当前 async 函数的执行,去处理其他任务。直到 somePromise 完成(无论是 fulfilled 还是 rejected),它才会回来继续执行。
    • 如果 Promise 成功了,await 会“解包”这个 Promise,并返回成功的值。
    • 如果 Promise 失败了,await 会抛出一个错误,这个错误可以被 try...catch 捕获。

🛠️ 任务:

  1. 创建一个名为 processUserData异步函数
  2. processUserData 函数内部,使用 try...catch 结构来处理可能发生的错误。
  3. try 代码块中:
    • 调用我们之前写的 fetchUserData(123),并使用 await 关键字来获取成功的结果。
    • 将获取到的用户数据打印到控制台。
  4. catch 代码块中:
    • 捕获可能发生的错误,并将其打印到控制台。
  5. 在主代码中,调用 processUserData 函数来启动整个流程。
  6. (可选挑战) 尝试在 try 块中也调用一次 fetchUserData(-1),看看 try...catch 是如何处理错误的。

📋 初始代码:
创建新文件 05-async-await.js。这次你需要从头开始写,但可以把上一关的 fetchUserData 函数复制过来,因为我们依然需要它。

// --- 首先,把上一关的 fetchUserData 函数复制到这里 ---
function fetchUserData(userId) {return new Promise((resolve, reject) => {if (userId <= 0) {reject("Invalid User ID");return;}setTimeout(() => {const user = {id: userId,name: 'John Doe',email: 'john.doe@example.com'};resolve(user);}, 2000);});
}// --- 在这里编写你的新代码 ---/*** 使用 async/await 来处理获取用户数据的流程。*/
async function processUserData() {// 1. 使用 try...catch 来包裹你的异步调用try {// 2. 使用 await 等待 fetchUserData(123) 的结果console.log("正在获取用户数据 (userId: 123)...");const user = "在这里使用 await 获取数据";// 3. 打印成功的结果console.log("成功获取到用户:", user);// --- 可选挑战 ---// console.log("正在尝试获取无效用户 (userId: -1)...");// const invalidUser = await fetchUserData(-1);// console.log("这一行永远不会被打印");} catch (error) {// 4. 捕获并打印错误console.error("在处理过程中发生错误:", error);}
}// --- 最后,调用你的 async 函数来启动整个流程 ---
console.log("程序开始...");
processUserData();
console.log("程序继续执行... (async 函数已调用,但内部代码在等待)");

✅ 预期输出:

程序开始...
正在获取用户数据 (userId: 123)...
程序继续执行... (async 函数已调用,但内部代码在等待)
// ...等待大约2秒...
成功获取到用户: { id: 123, name: 'John Doe', email: 'john.doe@example.com' }

如果你尝试了可选挑战,输出会是:

程序开始...
正在获取用户数据 (userId: 123)...
程序继续执行... (async 函数已调用,但内部代码在等待)
// ...等待大约2秒...
成功获取到用户: { id: 123, name: 'John Doe', email: 'john.doe@example.com' }
正在尝试获取无效用户 (userId: -1)...
在处理过程中发生错误: Invalid User ID

你会发现,代码的逻辑流看起来几乎和同步代码一模一样,非常直观。这就是 async/await 的魔力。

答案

好的,没问题。劳逸结合是高效学习的关键。我们今天就用答案和解析来收尾,明天你再亲手实践,印象会更深刻。

async/await 的美妙之处在于它能让你把异步代码写得像同步代码一样,逻辑清晰,易于阅读。

参考实现 (05-async-await.js)

// --- 首先,把上一关的 fetchUserData 函数复制到这里 ---
function fetchUserData(userId) {return new Promise((resolve, reject) => {// 增加一个错误检查,让 try...catch 能捕获到它if (userId <= 0) {reject(`Invalid User ID: ${userId}`);return;}// 模拟2秒的网络延迟setTimeout(() => {const user = {id: userId,name: 'John Doe',email: 'john.doe@example.com'};// Promise 成功,并返回 user 对象resolve(user);}, 2000);});
}// --- 在这里编写你的新代码 ---/*** 使用 async/await 来处理获取用户数据的流程。*/
async function processUserData() {console.log("processUserData 函数开始执行...");// 1. 使用标准的 try...catch 语句来包裹可能会出错的异步代码try {// 2. 使用 await 等待 fetchUserData(123) 的 Promise 完成//    代码会在这里“暂停”(非阻塞),直到拿到结果const user = await fetchUserData(123);// 3. 只有在 await 成功后,代码才会继续执行到这里console.log("✅ 成功获取到用户:", user);// --- 可选挑战的实现 ---console.log("---------------------------------");console.log("再次调用,尝试获取一个无效用户...");const invalidUser = await fetchUserData(-1);// 因为上一行会抛出错误,所以这一行永远不会被执行console.log("这一行永远不会被打印", invalidUser);} catch (error) {// 4. 如果 try 块中任何一个 await 的 Promise 被 reject,//    代码会立刻跳转到 catch 块中,并将 reject 的原因赋值给 errorconsole.error("❌ 在处理过程中捕获到错误:", error);}console.log("processUserData 函数执行完毕。");
}// --- 最后,调用你的 async 函数来启动整个流程 ---
console.log("程序开始...");
processUserData();
console.log("程序继续执行... (async 函数已调用,但其内部流程在等待)");

代码解析

  1. async function processUserData()

    • async 关键字告诉 JavaScript 引擎:“processUserData 是一个异步函数,它内部可能含有 await,并且这个函数本身会返回一个 Promise。”
  2. const user = await fetchUserData(123);

    • 这是 async/await 的核心。
    • await 做了什么?它告诉 processUserData 函数:“请在这里暂停执行,不要往下走了。去等待 fetchUserData(123) 这个 Promise 的结果。”
    • 如果 Promise 成功 (fulfilled)await 会像拆开礼物包装一样,把 Promise 对象里的成功值(也就是 user 对象)取出来,然后把它赋值给左边的 const user 变量。之后,函数继续往下执行。
    • 如果 Promise 失败 (rejected)await 会把 Promise 的失败原因(我们代码里的错误信息字符串)当作一个错误抛出 (throw)
  3. try...catch

    • 这就是 async/await 的优雅之处。因为失败的 Promise 会被 await 变成一个抛出的错误,所以我们可以用最传统、最熟悉的 try...catch 语句来捕获它!
    • await fetchUserData(-1) 执行时,Promise 被 rejectawait 抛出错误,try 块的执行立刻中断,程序流直接跳转到 catch 块,并将错误信息赋值给 error 变量。

对比 .then/.catch

看一下两种写法的直观对比:

Promise 链式写法:

fetchUserData(123).then(user => {console.log("成功:", user);}).catch(error => {console.error("失败:", error);});

async/await 写法:

try {const user = await fetchUserData(123);console.log("成功:", user);
} catch (error) {console.error("失败:", error);
}

async/await 的版本看起来就像在写普通的同步代码,从上到下,非常线性,并且用了我们早已熟悉的 try...catch 来处理错误,这大大降低了心智负担。


好的,今天的内容就到这里。async/await 是目前处理异步操作的社区标准和最佳实践,掌握它至关重要。

你先好好休息,我们明天再继续探索 JavaScript 的其他方面!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/912424.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/912424.shtml
英文地址,请注明出处:http://en.pswp.cn/news/912424.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PyTorch里.pt和.pth的区别

在PyTorch中&#xff0c;.pt和.pth文件均用于保存模型&#xff0c;但两者在设计初衷、存储内容和使用场景上存在差异。以下是详细对比&#xff1a; 1. 核心区别 特性.pt文件.pth文件存储内容完整模型&#xff08;结构参数优化器状态等&#xff09;仅模型参数&#xff08;state…

windows电脑如何安装iis作为部署服务器并

1.控制面板-程序-启用或关闭windows功能-勾选iis(缩写) 2.安装好iis后在开始中搜索iis就可以看见安装好了的管理器了

解锁ChatGPT高级玩法:模块化提示词库开发指南

目录 &#x1f50d; 一、引言&#xff1a;为什么需要模块化提示词库&#xff1f;&#x1f9f1; 二、模块化提示词库设计原理2.1 核心架构2.2 模块功能说明 ⚙️ 三、模块化提示词库开发实践&#xff08;附Python源码&#xff09;3.1 环境配置3.2 模块化提示词生成器3.3 提示词组…

Spring Boot 实现不同用户不同访问权限

前提 近期在使用 Spring Boot&#xff0c;用户角色被分为管理者和普通用户&#xff1b;角色不同&#xff0c;权限也就存在不同。 在 Spring Boot 里实现不同用户拥有不同访问权限&#xff0c;可借助 Spring Security 框架达成。 实现 1. 添加必要依赖 首先要在 pom.xml 里…

华沿协作机器人:数字孪生技术赋能焊接领域智能化升级

在工业4.0与智能制造浪潮的推动下&#xff0c;焊接行业正经历从传统工艺向数字化、柔性化转型的关键阶段。作为国内协作机器人领域的创新者&#xff0c;华沿机器人通过融合数字孪生、智能感知与多轴协同技术&#xff0c;在焊接场景中实现了技术突破与应用创新。本文将从技术原理…

Linux中部署Nacos保姆级教程

前置说明&#xff1a; Dokcer部署Nacos官方文档&#xff1a;Nacos Docker 快速开始 | Nacos 官网 一、Nacos版本说明 Nacos 1.x 版本 Nacos 1.1.3 &#xff1a;是一个相对稳定的版本&#xff0c;在一段时期内被广泛使用&#xff0c;但目前该版本已经下线&#xff0c;不再单独维…

战神授权后台报错:Parse error: syntax error, unexpected end of file in解决办法

问题现象分析 当您在战神授权后台遇到"Parse error: syntax error, unexpected end of file"这个错误时&#xff0c;说明PHP解析器在解析脚本文件时遇到了意外结束的情况。这种错误通常发生在PHP代码结构不完整时&#xff0c;比如缺少闭合的大括号、分号或者PHP结束…

HTML<span>元素详解

HTML<span>元素详解 <span> 是 HTML 中最常用的内联(inline)容器元素&#xff0c;用于对文档中的部分文本或内容进行标记和样式化。 一、基本语法 <span>内容</span>二、主要特点 内联元素&#xff1a;不会独占一行&#xff0c;只占据内容所需宽度无…

vscode ssh远程连接到Linux并实现免密码登录

vscode ssh远程连接到Linux并实现免密码登录 文章目录 vscode ssh远程连接到Linux并实现免密码登录一、安装VSCode扩展二、Linux侧工作三、连接四、实现免密登录 一、安装VSCode扩展 扩展一栏搜索remote找到Remote Development插件直接点击Install安装即可 二、Linux侧工作 U…

超级详细 的 Apache Camel 教程

前言 通过本教程学习 Apache Camel 的基础知识并在 Spring Boot 项目上创建您的第一个 Camel。 想开始使用Apache Camel吗&#xff1f;这是我关于这个流行的 Java 集成框架的教程。 我为完整的初学者编写了这个 Apache Camel 教程。它向您介绍了 Camel 的核心概念&#xff0c;并…

使用GithubActions和腾讯CloudBase自动发布静态网页

腾讯 CloudBase 可以用于托管静态网站&#xff0c;服务开通之后&#xff0c;使用 CloudBase CLI 可以将本地静态网站上传到 CloudBase&#xff0c;并生成相应的访问域名。 配置 Workflow 创建 .github/workflows/deploy.yml 文件, 编辑内容如下&#xff1a; name: Deploy to…

《聊一聊ZXDoc》之汽车标定、台架标定、三高标定

ZXDoc支持XCP/CCP标定功能&#xff0c;标定工作贯穿主机厂与Tier1厂商汽车ECU研发、生产、测试的整个流程&#xff0c;是保障ECU性能达标、功能稳定的关键。 什么是XCP/CCP标定&#xff1f; XCP/CCP标定是汽车电子领域用于ECU标定和测量的核心通信协议&#xff0c;由ASAM组织…

【目标检测】评估指标详解:Precision/Recall/F1-Score

&#x1f9d1; 博主简介&#xff1a;曾任某智慧城市类企业算法总监&#xff0c;目前在美国市场的物流公司从事高级算法工程师一职&#xff0c;深耕人工智能领域&#xff0c;精通python数据挖掘、可视化、机器学习等&#xff0c;发表过AI相关的专利并多次在AI类比赛中获奖。CSDN…

【unity游戏开发——网络】网络协议、TCP vs UDP 本质区别

注意&#xff1a;考虑到热更新的内容比较多&#xff0c;我将热更新的内容分开&#xff0c;并全部整合放在【unity游戏开发——网络】专栏里&#xff0c;感兴趣的小伙伴可以前往逐一查看学习。 文章目录 一、网络协议概述二、OSI七层模型三、TCP/IP四层模型四、核心传输协议对比…

Spark Streaming 与 Flink 实时数据处理方案对比与选型指南

Spark Streaming 与 Flink 实时数据处理方案对比与选型指南 实时数据处理在互联网、电商、物流、金融等领域均有大量应用&#xff0c;面对海量流式数据&#xff0c;Spark Streaming 和 Flink 成为两大主流开源引擎。本文基于生产环境需求&#xff0c;从整体架构、编程模型、容…

鸿蒙HarmonyOS 5小游戏实践:记忆翻牌(附:源代码)

记忆翻牌游戏是一款经典的益智游戏&#xff0c;它能有效锻炼玩家的记忆力和观察能力。本文将详细介绍如何使用鸿蒙&#xff08;HarmonyOS&#xff09;的ArkUI框架开发一款完整的记忆翻牌游戏&#xff0c;涵盖游戏设计、核心逻辑实现和界面构建的全过程。 游戏设计概述 记忆翻牌…

【Linux庖丁解牛】— 文件系统!

1 引⼊"块"概念 其实硬盘是典型的“块”设备&#xff0c;操作系统读取硬盘数据的时候&#xff0c;其实是不会⼀个个扇区地读取&#xff0c;这样 效率太低&#xff0c;⽽是⼀次性连续读取多个扇区&#xff0c;即⼀次性读取⼀个”块”&#xff08;block&#xff09;。…

如何通过自动化减少重复性工作

通过自动化减少重复性工作的关键策略包括&#xff1a;1、识别可被规则化操作的任务、2、引入RPA&#xff08;机器人流程自动化&#xff09;工具、3、整合AI与业务流程系统、4、部署脚本与低代码平台、5、持续优化自动化场景与效率。 其中&#xff0c;“引入RPA工具”被广泛认为…

知识变现全链路设计:从IP打造到商业闭环的系统方法论|创客匠人

一、变现低效根源&#xff1a;碎片化努力为何换不来持续增长&#xff1f; 创客匠人服务上千位知识创业者后发现&#xff0c;变现乏力多因缺乏系统设计&#xff1a;某营销专家的课程因定位模糊、表达生硬、渠道单一&#xff0c;低价仍少有人问。文档中提出的“六大超级设计公式…

如何利用人工智能大模型提升流量质量

摘要 流量质量是衡量数字化营销效果的重要指标之一&#xff0c;它反映了用户对网站或应用的兴趣和满意度。流量质量的常用评估方法有点击率、跳出率和用户停留时间等。本文将介绍如何利用人工智能大模型来分析和优化这些指标&#xff0c;提高流量质量&#xff0c;从而提升数字…