异步编程工具Promise与Async/Await:解决前端开发中的嵌套回调地狱

文章目录

    • Promise:处理异步操作的基本工具
      • Promise.all
    • async/await:更简洁的异步编程方式
    • Promise与async/await的比较
    • 结论

当谈及JavaScript中的异步编程时,两个非常常见且强大的工具是Promise和async/await。在本文中,我们将以实际例子中来讨论这两个概念,并探索它们在前端开发中的应用。

先看下面这个例子,还不会使用Promise和async/await之前我就是写下面这样的代码的~

// 使用回调函数来获取一个用户的信息和他的好友列表
function getUser(id, callback) {
  // 模拟一个异步的请求
  setTimeout(() => {
    // 假设id为1的用户存在,其他的用户不存在
    if (id === 1) {
      callback(null, { id: 1, name: "Alice" });
    } else {
      callback(new Error("User not found"));
    }
  }, 1000);
}

function getFriends(user, callback) {
  // 模拟一个异步的请求
  setTimeout(() => {
    // 假设用户Alice有两个好友,其他的用户没有好友
    if (user.name === "Alice") {
      callback(null, [{ id: 2, name: "Bob" }, { id: 3, name: "Charlie" }]);
    } else {
      callback(null, []);
    }
  }, 1000);
}



使用嵌套的回调函数来处理结果

// 使用嵌套的回调函数来处理结果
getUser(1, (error, user) => {
  if (error) {
    console.error("Error:", error);
  } else {
    console.log("User:", user);
    getFriends(user, (error, friends) => {
      if (error) {
        console.error("Error:", error);
      } else {
        console.log("Friends:", friends);
      }
    });
  }
});

可以看到上面这个就是一个经典的回调地狱,它(callback hell)是指在JavaScript中使用嵌套的回调函数来处理异步操作的一种编程风格,这种编程风格会导致代码层级过深,难以理解和维护,也不利于错误处理和异常捕获。这就是回调地狱的问题。为了解决这个问题,我们可以使用Promise或async/await等方法来改进代码,让异步操作更加优雅和简洁。

在这里插入图片描述

Promise:处理异步操作的基本工具

Promise是一种用于处理异步操作的对象。它代表了一个可能尚未完成的操作,并可以在操作完成或失败后采取相应的行动。Promise对象有三种状态:待定(pending)、已完成(fulfilled)和已拒绝(rejected)。

在使用Promise时,我们可以使用new Promise()构造函数来创建一个Promise对象。构造函数接受一个执行器函数作为参数,该函数包含两个参数:resolve和reject。通过调用resolve函数,我们可以将Promise从待定状态转换为已完成状态,并传递一个结果值;而通过调用reject函数,我们可以将Promise从待定状态转换为已拒绝状态,并传递一个错误原因。

Promise提供了一组方法,使我们能够在Promise对象上执行各种操作。其中一些方法包括:

  • .then(): 当Promise对象状态为已完成时,可以使用该方法指定一个回调函数来处理结果。
  • .catch(): 当Promise对象状态为已拒绝时,可以使用该方法指定一个回调函数来处理错误。
  • .finally(): 该方法在Promise对象无论状态如何都会执行,无论是已完成还是已拒绝。

使用Promise进行异步编程时,可以将多个Promise对象链接在一起,形成一个Promise链。这样可以按顺序执行异步操作,并在每个操作完成后传递结果到下一个操作。

接下来我们将上述代码使用Promise进行改写

getUser(1)
  .then((user) => {
    console.log("User:", user);
    return getFriends(user);
  })
  .then((friends) => {
    console.log("Friends:", friends);
  })
  .catch((error) => {
    console.error("Error:", error);
  });

尽管Promise是一种强大的工具,但它的语法相对较为冗长,尤其是在处理多个异步操作时。为了解决这个问题,ES2017引入了async/await。

Promise.all

Promise.all是一个静态方法,它可以接收一个Promise对象的数组作为参数,返回一个新的Promise对象,该对象的状态和结果取决于数组中的所有Promise对象的状态和结果。

如果数组中的所有Promise对象都变为fulfilled(已成功)状态,那么Promise.all返回的Promise对象也会变为fulfilled状态,其结果是一个数组,包含了数组中的所有Promise对象的结果。

如果数组中有任何一个Promise对象变为rejected(已失败)状态,那么Promise.all返回的Promise对象也会变为rejected状态,其结果是第一个变为rejected状态的Promise对象的结果。Promise.all可以用来处理多个异步操作的结果,比如并行发送多个网络请求,等待多个定时器完成,等等。

// 使用Promise.all来并行发送三个网络请求,获取三个用户的信息
function getUser(id) {
  return new Promise((resolve, reject) => {
    // 模拟一个异步的请求
    setTimeout(() => {
      // 假设id为1,2,3的用户存在,其他的用户不存在
      if (id === 1 || id === 2 || id === 3) {
        resolve({ id: id, name: `User${id}` });
      } else {
        reject(new Error("User not found"));
      }
    }, 1000 * id); // 模拟不同的响应时间
  });
}

// 使用Promise.all来处理三个用户的结果
Promise.all([getUser(1), getUser(2), getUser(3)])
  .then((users) => {
    console.log("Users:", users);
  })
  .catch((error) => {
    console.error("Error:", error);
  });

输出结果:

Users: [ { id: 1, name: 'User1' }, { id: 2, name: 'User2' }, { id: 3, name: 'User3' } ]

可以看到,Promise.all返回的Promise对象在三个网络请求都成功后变为fulfilled状态,其结果是一个包含三个用户信息的数组。如果其中有任何一个网络请求失败,那么Promise.all返回的Promise对象就会变为rejected状态,其结果是第一个失败的网络请求的错误。这样,我们就可以使用Promise.all来并行处理多个异步操作的结果,提高代码的效率和可读性。

async/await:更简洁的异步编程方式

async/await是建立在Promise之上的一种语法糖,旨在简化异步代码的编写和阅读。它允许我们以一种看起来同步的方式编写异步代码。

在使用async/await时,我们使用async关键字来定义一个异步函数,该函数可以包含一个或多个await表达式。在异步函数内部,我们可以使用await关键字来等待一个Promise对象的解析,并将其结果赋值给一个变量。在等待期间,函数的执行将暂停,直到Promise解析完成。

同样我们将上述代码使用Promise进行改写

// 使用async/await来处理结果
async function main() {
  try {
    let user = await getUser(1);
    console.log("User:", user);
    let friends = await getFriends(user);
    console.log("Friends:", friends);
  } catch (error) {
    console.error("Error:", error);
  }
}

main();

Promise与async/await的比较

Promise和async/await都是处理异步操作的强大工具,但它们在语法上有一些区别,因此适用于不同的场景。

Promise适用于以下情况:

  1. 处理较简单的异步操作,不需要嵌套太多层级。
  2. 在需要精细控制异步流程的情况下,使用Promise链可以更直观地表达代码逻辑。
  3. 在需要同时处理多个异步操作的情况下,Promise.all()方法可以等待多个Promise对象都解析完成。

而async/await则适用于以下情况:

  1. 处理复杂的异步操作,需要嵌套多层级或有多个依赖关系的异步操作。
  2. 在代码的可读性和可维护性上更注重,因为async/await提供了类似同步代码的写法,更容易理解和调试。
  3. 在需要按顺序执行异步操作的情况下,使用await关键字可以确保前一个操作完成后再执行下一个操作。

无论是Promise还是async/await,它们都在现代JavaScript开发中扮演着重要的角色。选择使用哪种方法取决于具体的需求和代码风格。

结论

  • 本文深入探讨了前端开发中的Promise和async/await,这两个工具都为处理异步操作提供了强大的支持。
  • Promise是一种用于处理异步操作的对象,它提供了一组方法来管理异步流程。通过Promise链,我们可以按顺序执行异步操作,并在每个操作完成后传递结果。
  • 而async/await是建立在Promise之上的一种语法糖,旨在简化异步代码的编写和阅读。它允许我们以一种看起来同步的方式编写异步代码,提高了代码的可读性和可维护性。
  • 根据具体的需求和代码风格,选择适合的工具来处理异步操作。在实际开发中,我们可以根据情况灵活使用Promise和async/await,以提高开发效率和代码质量。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

​软考-高级-系统架构设计师教程(清华第2版)【第3章 信息系统基础知识(p120~159)-思维导图】​

软考-高级-系统架构设计师教程(清华第2版)【第3章 信息系统基础知识(p120~159)-思维导图】 课本里章节里所有蓝色字体的思维导图

【Redis】String字符串类型

上一篇:Redis-key的使用 https://blog.csdn.net/m0_67930426/article/details/134361821?spm1001 .2014.3001.5501 目录 appen (附加) strlen(获取字符串的长度) incr decr getRange(获取字符串) setRange(替…

Flink

1. Flink简介 1.1 初识Flink Flink项目的理念是:“Apache Flink是为分布式、高性能、随时可用以及准确的流处理应用程序打造的开源的有状态的流处理框架”。 Apache Flink是一个框架和分布式处理引擎,用于对无界和有界数据流进行有状态计算。Fl…

fastbins_Double Free调试

我大哥给我出了一题pwn heap题,当时现场的时候没解出来,想岔了。事后感概自己还是理解的不够透彻。决定认真再次调试下。用的是2.23版本的how2heap中fastbins_dup.c。 简单用gcc编译下,然后带源码调试: 生成一个a.out的文件&#…

leetcode刷题日记:111. Minimum Depth of Binary Tree(二叉树的最小深度)

给我们一个二叉树,我们应该如何来求二叉树的最小深度呢? 二叉树的最小深度指的是叶子结点到所处的位置最小的,这就是二叉树的最小深度,也就是说我们要找的是离根结点最近的叶子结点。如果我们从根结点向下出发寻找叶子节点&#x…

移动硬盘和u盘的区别哪个好 移动硬盘和u盘有啥区别

在数字时代的今天,数据存储已经成为我们生活中的重要一环。当我们需要移动、备份或传输大量数据时,常常会不知道是选择移动硬盘还是U盘。其实,对于许多人来说,移动硬盘和U盘之间的区别并不清晰。下面我们就来看移动硬盘和u盘的区别…

思科9300交换机使用USB进行升级ISO

一、下载ISO 一、网址 Software Download - Cisco Systems 二、找到型号 四、选择XE 软件 五、进行下载 二、COPY 进 U盘 一、、请注意!如果你的U盘不是Fat32文件格式则交换机读取不了,请先格式化再复制文件。 二、下载后将 bin文件复制到U盘。 1.扩展…

经典OJ题:重排链表

题目: 给定一个链表,在进行重排前: 进行重排链表后: 如上图所示,所谓的重拍链表,就是将第一个节点连接第倒数第一个节点,第二个节点连接倒数第二个节点,以此类推,最后在连…

【matlab】KMeans KMeans++实现手写数字聚类

目录 matlab代码kmeans matlab代码kmeans MNIST DATABASE下载网址: http://yann.lecun.com/exdb/mnist/ 聚类 将物理或抽象对象的集合分成由类似特征组成的多个类的过程称为聚类(clustering)。 对于给定N个n维向量x1,…,xN∈Rn,聚类的目标…

贪心

【深基12.例1】部分背包问题 题目描述 阿里巴巴走进了装满宝藏的藏宝洞。藏宝洞里面有 N ( N ≤ 100 ) N(N \le 100) N(N≤100) 堆金币,第 i i i 堆金币的总重量和总价值分别是 m i , v i ( 1 ≤ m i , v i ≤ 100 ) m_i,v_i(1\le m_i,v_i \le 100) mi​,vi​(…

统计学_蒙特卡罗方法

1、蒙特卡罗方法的基本思想 蒙特卡罗方法(Monte Carlo method)是由冯诺依曼和乌拉姆等人发明的,“蒙特卡罗”这个名字是出自摩纳哥的蒙特卡罗赌场,这个方法是一类基于概率的方法的统称,不是特指一种方法。 蒙特卡罗方法也成统计模拟方法&am…

SpringBoot3数据访问

SpringBoot3数据访问 SpringBoot整合 Spring、SpringMVC、MyBatis进行数据访问开发。 整合SSM场景 整合步骤 1、创建SSM整合项目 ①数据库准备 DROP TABLE IF EXISTS t_user; CREATE TABLE t_user (id bigint NOT NULL AUTO_INCREMENT COMMENT 编号,login_name varchar(200)…

前端---认识HTML

文章目录 什么是HTML?HTML的读取、运行HTML的标签注释标签标题标签段落标签换行标签格式化标签图片标签a标签表格标签列表标签表单标签form标签input标签文本框单选框复选框普通按钮提交按钮文件选择框 select标签textarea标签特殊标签div标签span标签 什么是HTML&a…

2.1 CE修改器:精确数值扫描

本关是CE修改器的第一关,用户需要通过 Cheat Engine 工具完成精确扫描值。在这个练习中,需要将一个特定的数值(健康值)改变为 1000。首先,要确保数值类型设置正确,默认的是2字节或4字节。接着,选…

(三)正点原子I.MX6ULL kernel6.1挂根文件系统

一、概述 移植NXP官方最新的linux kernel(linux-imx-lf-6.1.y) 移植方法基本参照正点原子教程 移植开发板:正点原子阿尔法2.1 二、添加开发板到内核 进入内核目录下,先修改Makefile 打开终端: cp arch/arm/configs/im…

Nginx:如何实现一个域名访问多个项目

1. 背景介绍 最近在多个项目部署中遇到这样一个问题,一个域名如何实现多个项目的访问。因为不想自己单独去申请域名证书和域名配置,便想到了这个方案,结合Nginx的location功能实现了自己的需求,便记录下来。示例中是以项目演示&a…

Unity - 各向异性 - 丝绸材质

文章目录 目的环境主观美术效果的[假]丝绸基于物理的方式ProjectPBR filament web captureReferences 目的 拾遗,备份 环境 Unity : 2020.3.37f1 Pipeline : Builtin Rendering Pipeline 主观美术效果的[假]丝绸 非常简单 : half specualr pow(1 - NdotV, _Edg…

RT-Thread Studio开发 新手入门

文章目录 前言一、RT-Thread Studio 与 STM32CubeMX 下载安装二、新建工程三、点亮LED灯四、按键中断五、串口通信六、OLED显示 前言 软件开发环境:RT-Thread Studio、STM32CubeMX 硬件:STM32F407ZGT6 一、RT-Thread Studio 与 STM32CubeMX 下载安装 …

【C语言数据结构————————二叉树】

文章目录 文章目录 一、什么是树 树的定义 树的种类 树的深度 树的基本术语 二、满二叉树 定义 满二叉树的特点 三、完全二叉树 定义 特点 四、二叉树的性质 五、二叉树的存储结构 顺序存储结构 链式存储结构 六、二叉树的基本操作 七、二叉树的创建 八、二叉树…

电容的作用

文章目录 总结1.降压2.滤波3.延时4.耦合5.旁路电容 总结 1.降压 问题: 直接连接灯泡会烧掉 解决方案 进一步为了防止电容放电,伤人,加入一个大电阻 2.滤波 直流的情况 交流的情况 频率与容抗的关系 3.延时 4.耦合 滤除直流成分&#xf…