深入理解 JavaScript 中的 Promise、async 和 await

序言

在 JavaScript 中,异步编程是一项关键技能。为了更有效地处理异步任务,JavaScript 在其生命周期中引入了一系列功能。其中,Promise、async 和 await 是现代 JavaScript 中最重要的异步编程工具之一。本文将深入探讨这些概念,帮助大家更好地理解它们的作用和用法。

一、回调地狱

回调地狱(callback hell)是指在异步操作中过度嵌套回调函数,导致代码难以阅读和维护的情况。在过去,JavaScript 中处理异步操作通常使用回调函数。回调函数有一些缺点,比如难以管理,导致了回调地狱的问题,代码不易阅读和维护。

// 回调地狱示例中用到的 fetchData 函数的定义
const fetchData = (callback) => {setTimeout(() => {const success = Math.random() > 0.5; // 模拟操作成功或失败if (success) {callback('Data fetched successfully');} else {callback('Failed to fetch data');}}, Math.random() * 2000); // 模拟不同的执行时间
};// 示例:回调地狱
fetchData((data) => {console.log(data); // 输出第一个异步操作的结果fetchData((data) => {console.log(data); // 输出第二个异步操作的结果fetchData((data) => {console.log(data); // 输出第三个异步操作的结果// 更多嵌套...}, (error) => {console.error(error); // 输出可能的错误信息});}, (error) => {console.error(error); // 输出可能的错误信息});
}, (error) => {console.error(error); // 输出可能的错误信息
});

二、Promise

Promise 是 JavaScript 中处理异步操作的一种强大机制。它允许处理尚未完成的操作,并在操作完成或失败时执行相应的操作。通过 Promise,可以更清晰地表达异步操作的流程,避免了传统的回调地狱问题。

Promise 是一个对象,代表了尚未完成但预计会在未来完成的操作。Promise 可以有三种状态:

  1. Pending(进行中):初始状态,表示操作尚未完成,也没有失败。
  2. Fulfilled(已成功):表示操作已经成功完成。
  3. Rejected(已失败):表示操作失败。

Promise 可以通过 then() 方法来处理成功的结果,通过 catch() 方法来处理失败的结果。这使得异步操作的处理更加清晰和可控。

// 使用 Promise 定义 fetchData 函数
const fetchData = () => {return new Promise((resolve, reject) => {setTimeout(() => {const success = Math.random() > 0.5; // 模拟操作成功或失败if (success) {resolve('Data fetched successfully');} else {reject('Failed to fetch data');}}, Math.random() * 2000); // 模拟不同的执行时间});
};// 示例:使用 Promise 处理异步操作
fetchData().then((data) => {console.log(data); // 输出 'Data fetched successfully'}).catch((error) => {console.error(error); // 输出 'Failed to fetch data'});

三、Promise 嵌套的 Promise 链

有时候,我们需要依次执行多个异步操作,其中的每个操作依赖于前一个操作的结果。在这种情况下,可以使用 Promise 嵌套的 Promise 链。

// 示例:Promise 嵌套的 Promise 链
const fetchFirstData = () => {return new Promise((resolve, reject) => {// 模拟异步操作,2秒后返回结果setTimeout(() => {const success = Math.random() > 0.5; // 模拟操作成功或失败if (success) {resolve('First data fetched successfully');} else {reject('Failed to fetch first data');}}, 2000);});
};const fetchSecondData = () => {return new Promise((resolve, reject) => {// 模拟异步操作,2秒后返回结果setTimeout(() => {const success = Math.random() > 0.5; // 模拟操作成功或失败if (success) {resolve('Second data fetched successfully');} else {reject('Failed to fetch second data');}}, 2000);});
};fetchFirstData().then((firstData) => {// 输出第一个异步操作的结果console.log(firstData); // 输出 'First data fetched successfully'// 返回第二个异步操作的 Promise 对象return fetchSecondData().then((secondData) => {// 输出第二个异步操作的结果console.log(secondData); // 输出 'Second data fetched successfully'}).catch((error) => {// 输出第二个异步操作可能的错误信息console.error(error); // 输出可能的错误信息});}).catch((error) => {// 输出第一个异步操作可能的错误信息console.error(error); // 输出可能的错误信息});

四、async 和 await

尽管 Promise 解决了回调地狱的问题,但在处理多个异步操作时,可能会导致深层嵌套的 Promise 链,使得代码难以理解。为了解决这个问题,ECMAScript 2017 引入了 async 和 await。它们让异步代码更像同步代码,使得代码更加清晰和易于理解。

// 示例:使用 async 和 await 处理异步操作
const fetchFirstData = () => {return new Promise((resolve, reject) => {// 模拟异步操作,2秒后返回结果setTimeout(() => {const success = Math.random() > 0.5; // 模拟操作成功或失败if (success) {resolve('First data fetched successfully');} else {reject('Failed to fetch first data');}}, 2000);});
};const fetchSecondData = () => {return new Promise((resolve, reject) => {// 模拟异步操作,2秒后返回结果setTimeout(() => {const success = Math.random() > 0.5; // 模拟操作成功或失败if (success) {resolve('Second data fetched successfully');} else {reject('Failed to fetch second data');}}, 2000);});
};const fetchDataAsync = async () => {try {// 使用 await 等待第一个异步操作的结果const firstData = await fetchFirstData();// 输出第一个异步操作的结果console.log(firstData); // 输出 'First data fetched successfully'// 使用 await 等待第二个异步操作的结果const secondData = await fetchSecondData();// 输出第二个异步操作的结果console.log(secondData); // 输出 'Second data fetched successfully'} catch (error) {// 输出可能的错误信息console.error(error);}
};// 调用包含异步操作的函数
fetchDataAsync();

async 和 await 基于 Promise 的语法糖,它们可以让我们以同步的方式编写异步代码,尤其是在处理多个异步操作时,可以极大地提高代码的可读性和可维护性。

五、FAQ

Promise、async 和 await 是 JavaScript 中处理异步编程的核心工具。通过 Promise,可以更灵活地处理异步操作的状态和结果。而 async 和 await 则使异步代码的编写和理解更加直观和简洁。

推荐阅读

  1. 深入探究 Spring Boot Starter:从概念到实践
  2. RBAC 权限设计(五)
  3. Docker Compose:简化多容器应用部署
  4. cURL:命令行下的网络工具
  5. RabbitMQ(Docker 单机部署)

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

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

相关文章

WebAssembly在现代前端中的应用与未来展望

WebAssembly(简称WASM)在现代前端开发中的应用日益广泛,其核心优势在于提供了一种高性能、跨平台的执行环境,使得非JavaScript语言编写的代码也能在Web浏览器中运行。以下是WebAssembly在现代前端应用的一些关键领域及其未来展望&…

Java | Leetcode Java题解之第83题删除排序链表中的重复元素

题目: 题解: class Solution {public ListNode deleteDuplicates(ListNode head) {if (head null) {return head;}ListNode cur head;while (cur.next ! null) {if (cur.val cur.next.val) {cur.next cur.next.next;} else {cur cur.next;}}return…

C++调用有依赖库的python函数(VS2017+WIN10+Anaconda虚拟环境)

情况1.在写的函数中依赖了能够pip的库,例如numpy库、torch库,见下面的函数: import numpy as np import torch def add1(a, b):# 确保a和b都是NumPy数组a_array np.array(a) if not isinstance(a, np.ndarray) else ab_array np.array(b) if not isins…

萤火虫优化算法(Firefly Algorithm)

注意:本文引用自专业人工智能社区Venus AI 更多AI知识请参考原站 ([www.aideeplearning.cn]) 算法背景 萤火虫优化算法,是由剑桥大学的Xin-She Yang在2009年提出的一种基于群体智能的优化算法。它的灵感来源于萤火虫在夜晚闪烁…

Blender细节补充

1.饼状菜单,用于快速切换/选择 例如: ~:切换视图 Z:切换着色方式 ,:切换坐标系 .:切换基准点 Shift S:吸附 有两种使用方式: -点选 -滑选,按快捷键…

表的创建与操作表

1. 创建表 创建表有两种方式 : 一种是白手起家自己添,一种是富二代直接继承. 2. 创建方式1 (1). 必须具备条件 CREATE TABLE权限存储空间 (2). 语法格式 CREATE TABLE IF NOT EXISTS 表名(字段1, 数据类型 [约束条件] [默认值],字段2, 数据类型 [约束条件] [默…

node pnpm修改默认包的存储路径

pnpm与npm的区别 PNPM和NPM是两个不同的包管理工具。 NPM(Node Package Manager)是Node.js的官方包管理工具,用于安装、发布和管理Node.js模块。NPM将包安装在项目的node_modules目录中,每个包都有自己的依赖树。 PNPM&#xf…

Flowable常用API

Flowable常用API RepositoryService RepositoryService很可能是使用Flowable引擎要用的第一个服务。这个服务提供了管理与控制部署(deployments)与流程定义(process definitions)的操作.一个部署中可以包含多个BPMN 2.0 XML文件及其他资源. RuntimeService TaskService 查…

【Bug】Clash出现端口0的情况

win版本的Docker桌面版用了Hyper-V的功能,虚拟机需要映射一部分端口,并且在系统更新后对动态映射的端口范围进行了更改,导致占用了本来的7890Clash使用的端口。 cmd去查看还能使用的端口 netsh interface ipv4 show excludedportrange prot…

从0开发、发布油猴脚本(保姆级)

概览 项目中使用conify集成图标,有些内网用户只能使用离线图标,但是如何判断使用的conify集成图标是在线还是离线呢?这个时候就需要一个油猴脚本,作用于iconify官网,对离线图标进行标识。 此篇文章主要从如下几点去梳…

HIVE调优-分区分桶,合并小文件

HIVE调优-分区分桶,合并小文件 目录 HIVE调优-分区分桶,合并小文件 1.分区分桶,合并小文件 1)为什么小文件需要合并? 2)如何合并小文件: 1.分区分桶,合并小文件 1)为什…

MySQL数据库的初始化(创建库、创建表、向数据库添加测试数据)

MySQL数据库的初始化(创建库、创建表、向数据库添加测试数据) MySQL数据库简介MySQL创建一个新的数据库MySQL创建一张新的数据表简单(设置)表复杂(设置)表 填充测试数据SQL语句mysql>模式下输入的每句sq…

现代信号处理9_正则化(CSDN_20240512)

正则化的引入 解线性方程组: 这项工作有很多种做法,下面介绍两种,如下图所示,有一些数据点需要拟合,拟合的方法有很多。 1) 构造线性函数①,这种函数比较简单,此时 2) 构…

跟TED演讲学英文:Why US politics is broken — and how to fix it by Andrew Yang

Why US politics is broken — and how to fix it Link: https://www.ted.com/talks/andrew_yang_why_us_politics_is_broken_and_how_to_fix_it? Speaker: Andrew Yang Date: April 2024 文章目录 Why US politics is broken — and how to fix itIntroductionVocabularyTr…

物联网与JavaScript:JavaScript在物联网领域中的应用,使用Node.js和JavaScript来控制硬件设备

当我们谈到物联网(IoT)时,我们通常会想到硬件和嵌入式编程语言,比如C或C。然而,JavaScript通过Node.js,也能够在物联网领域发挥作用。 Node.js是一个能够在服务器端运行JavaScript的平台,这使得…

wordpress主题 7B2 PRO主题5.4.2免授权直接安装

内容目录 一、详细介绍二、效果展示1.部分代码2.效果图展示 三、学习资料下载 一、详细介绍 WordPress 资讯、资源、社交、商城、圈子、导航等多功能商用主题:B2 PRO 其设计风格专业且时尚,功能十分强大,包括多栏布局、自定义页面、强大的主…

初始化VUE项目Sorry, name can no longer contain capital letters

安装过VUE脚手架,创建项目 vue init webpack vueDemoOne 报错 Sorry, name can no longer contain capital letters 翻译了一下,意思就是项目名不能包含大写字母,修改原来的驼峰命名,修改后成功创建。 vue init webpack vue_de…

【JavaEE网络】HTTPS详解:从对称与非对称加密到证书认证

目录 HTTPSHTTPS 是什么“加密” 是什么HTTTPS 的工作过程引入对称加密引入非对称加密引入证书完整流程总结 HTTPS HTTPS 是什么 HTTPS 也是一个应用层协议. 是在 HTTP 协议的基础上引入了一个加密层. HTTP 协议内容都是按照文本的方式明文传输的. 这就导致在传输过程中出现…

LeetCode - 0088 合并两个有序数组

题目地址:https://leetcode.cn/problems/merge-sorted-array/description/ 引言:话接上回,由于上次面试官着急下班,面试不得不提前终止,这不,他又找我去面试了 面试官:你好,小伙子&a…

Redis——Java三种客户端(Jedis、Lettuce和Redisson)

Redis在Java领域有着广泛的应用,为了更方便地与Redis进行交互,开发者们创建了多种Java客户端。其中,Jedis、Lettuce和Redisson是三种最为流行的Redis Java客户端。以下是关于这三种客户端的简要介绍: Jedis: Jedis是…