Promise和async/await

news2024/11/19 13:41:21

1、回调地狱

多层回调函数的相互嵌套,就形成了回调地狱。示例代码如下:

回调地狱的缺点: 

  •  代码耦合性太强,牵一发而动全身,难以维护
  • 大量冗余的代码相互嵌套,代码的可读性变差

1.1、如何解决回调地狱的问题

为了解决回调地狱的问题,ES6ECMAScript 2015)中新增了 Promise 的概念。

1.2、Promise 的基本概念

Promise 是一个构造函数

  • 我们可以创建 Promise 的实例 const p = new Promise()
  • new 出来的 Promise 实例对象,代表一个异步操作

Promise.prototype 上包含一个 .then() 方法

  • 每一次 new Promise() 构造函数得到的实例对象,都可以通过原型链的方式访问到 .then() 方法,例如 p.then()

.then() 方法用来预先指定成功和失败的回调函数

  • p.then(成功的回调函数失败的回调函数)
  • p.then(result => { }, error => { })
  • 调用 .then() 方法时,成功的回调函数是必选的、失败的回调函数是可选的

2、基于回调函数按顺序读取文件内容

 

3、基于 then-fs 读取文件内容

        由于 node.js 官方提供的 fs 模块仅支持回调函数的方式读取文件,不支持 Promise 的调用方式。因此,需要先运行如下的命令,安装 then-fs 这个第三方包,从而支持我们基于 Promise 的方式读取文件的内容:

npm install then-fs

3.1、then-fs 的基本使用

        调用 then-fs 提供的 readFile() 方法,可以异步地读取文件的内容,它的返回值是 Promise 的实例对象。因此可以调用 .then() 方法为每个 Promise 异步操作指定成功失败之后的回调函数。示例代码如下:

/**
 * 基于 Promise 的方式读取文件
*/
import thenFs from 'then-fs'

// 注意:.then() 中的失败回调是可选的,可以被省略
thenFs.readFile('./files/1.txt', 'utf8').then((r1) => {console.log(r1)})
thenFs.readFile('./files/2.txt', 'utf8').then((r2) => {console.log(r2)})
thenFs.readFile('./files/3.txt', 'utf8').then((r3) => {console.log(r3)})

注意:上述的代码无法保证文件的读取顺序,需要做进一步的改进!

3.2、.then() 方法的特性

        如果上一个 .then() 方法中返回了一个新的 Promise 实例对象,则可以通过下一个 .then() 继续进行处理。通过 .then() 方法的链式调用,就解决了回调地狱的问题。

3.3、基于 Promise 按顺序读取文件的内容

Promise 支持链式调用,从而来解决回调地狱的问题。示例代码如下:

import thenFs from 'then-fs'

thenFs
    .readFile('./files/11.txt', 'utf8')  // 1、返回值是 Promise 的实例对象
    .then((r1) => { // 2、通过 .then 为第一个 Promise 实例指定成功之后的回调函数
        console.log(r1)
        return thenFs.readFile('./files/2.txt', 'utf8')  // 3、在第一个 .then 中返回一个新的 Promise 实例对象
    })
    .then((r2) => { // 4、继续调用 .then 为上一个 .then 的返回值(新的 Promise 实例)指定成功之后的回调函数
        console.log(r2)
        return thenFs.readFile('./files/3.txt', 'utf8') // 5、在第二个 .then 中在返回一个新的Promise实例对象
    })
    .then((r3) => { // 6、继续调用 .then 为上一个 .then 的返回值(新的 Promise 实例)指定成功之后的回调函数
        console.log(r3)
    })

3.4、通过 .catch 捕获错误

  • 在 Promise 的链式操作中如果发生了错误,可以使用 Promise.prototype.catch 方法进行捕获和处理:
  • 如果不希望前面的错误导致后续的 .then 无法正常执行,则可以将 .catch 的调用提前,示例代码如下: 
import thenFs from 'then-fs'

thenFs
    .readFile('./files/11.txt', 'utf8')
    .catch((err) => {       // 捕获第一行发生的错误,并输出错误的消息
        console.log(err.message)    // 由于错误已被及时处理,不影响后续 .then 的正常运行
    })
    .then((r1) => {
        console.log(r1)
        return thenFs.readFile('./files/2.txt', 'utf8')
    })
    .then((r2) => {
        console.log(r2)
        return thenFs.readFile('./files/3.txt', 'utf8')
    })
    .then((r3) => {
        console.log(r3)
    })

3.5、Promise.all() 方法

        Promise.all() 方法会发起并行的 Promise 异步操作,等所有的异步操作全部结束后才会执行下一步的 .then 操作(等待机制)。示例代码如下:

import thenFs from 'then-fs'

// 定义一个数组,存放 3 个读文件的异步操作
const promiseArr = [
  thenFs.readFile('./files/3.txt', 'utf8'),
  thenFs.readFile('./files/2.txt', 'utf8'),
  thenFs.readFile('./files/1.txt', 'utf8'),
]

// 将 Promise 的数组,作为 Promise.all() 的参数
Promise.all(promiseArr).then(result => { // 所有文件读取成功(等待机制)
  console.log(result)
})

注意:数组中 Promise 实例的顺序,就是最终结果的顺序!

3.6、Promise.race() 方法

        Promise.race() 方法会发起并行的 Promise 异步操作,只要任何一个异步操作完成,就立即执行下一步的 .then 操作(赛跑机制)。示例代码如下:

import thenFs from 'then-fs'

// 定义一个数组,存放 3 个读文件的异步操作
const promiseArr = [
  thenFs.readFile('./files/3.txt', 'utf8'),
  thenFs.readFile('./files/2.txt', 'utf8'),
  thenFs.readFile('./files/1.txt', 'utf8'),
]

// 将 Promise 的数组,作为 Promise.race() 的参数
Promise.race(promiseArr).then(result => { // 只要任何一个异步操作完成,就立即执行成功回调函数(赛跑机制)
  console.log(result)
})

4、基于 Promise 封装读文件的方法

方法的封装要求:

  • 方法的名称要定义为 getFile
  • 方法接收一个形参 fpath,表示要读取的文件的路径
  • 方法的返回值Promise 实例对象

4.1、getFile 方法的基本定义

// 1、方法的名称 为 getFile
// 2、方法接收一个形参 fpath,表示要读取的文件路径
function getFile(fpath) {
    // 3、方法的返回值为Promise 的实例对象
    return new Promise()
}

注意:第 5 行代码中的 new Promise() 只是创建了一个形式上的异步操作

4.2、创建具体的异步操作

        如果想要创建具体的异步操作,则需要在 new Promise() 构造函数期间,传递一个 function 函数将具体的异步操作定义到 function 函数内部。示例代码如下:

// 1、方法的名称 为 getFile
// 2、方法接收一个形参 fpath,表示要读取的文件路径
function getFile(fpath) {
    // 3、方法的返回值为Promise 的实例对象
    return new Promise(function() {
        // 下面这行代码,表示这是一个具体的读文件的异步操作
        fs.readFile(fpath, 'utf8', (err, dataStr) => {})
 })
}

4.3、获取 .then 的两个实参

通过 .then() 指定的成功失败的回调函数,可以在 function 形参中进行接收,示例代码如下:

4.4、调用 resolve reject 回调函数 

Promise 异步操作的结果,可以调用 resolve reject 回调函数进行处理。示例代码如下:

import fs from 'fs'

function getFile(fpath) {
  // resolve是“成功的”回调函数,reject是“失败的”回调函数
  return new Promise(function (resolve, reject) {
    fs.readFile(fpath, 'utf8', (err, dataStr) => {
      if (err) return reject(err) // 如果读取失败,则调用“失败的回调函数”
      resolve(dataStr)        // 如果读取成功,则调用“成功的回调函数”
    })
  })
}

getFile('./files/11.txt')
  .then((r1) => {
    console.log(r1)
  }) // then方法中,错误的回调函数可以省略,我们可以使用catch方法来捕获异常
  .catch((err) => console.log(err.message))

5、什么是 async/await

        async/await ES8ECMAScript 2017)引入的新语法,用来简化 Promise 异步操作。在 async/await 出现之前,开发者只能通过链式 .then() 的方式处理 Promise 异步操作。示例代码如下:

import thenFs from 'then-fs'

thenFs
    .readFile('./files/11.txt', 'utf8')
    .catch((err) => {
        console.log(err.message)
    })
    .then((r1) => {
        console.log(r1)
        return thenFs.readFile('./files/2.txt', 'utf8')
    })
    .then((r2) => {
        console.log(r2)
        return thenFs.readFile('./files/3.txt', 'utf8')
    })
    .then((r3) => {
        console.log(r3)
    })
  • .then 链式调用的优点:解决了回调地狱的问题
  • .then 链式调用的缺点:代码冗余、阅读性差、不易理解

6、async/await 的基本使用

使用 async/await 简化 Promise 异步操作的示例代码如下:

import thenFs from 'then-fs'


// 按照顺序读取文件 1,2,3
async function getAllFile() {
    const r1 = await thenFs.readFile('./files/1.txt', 'utf8')
    console.log(r1)
    const r2 = await thenFs.readFile('./files/2.txt', 'utf8')
    console.log(r2)
    const r3 = await thenFs.readFile('./files/3.txt', 'utf8')
    console.log(r3)
}

getAllFile()

async/await 使用注意事项

  1. 如果在 function 中使用了 await,则 function 必须被 async 修饰
  2. 在 async 方法中,第一个 await 之前的代码会同步执行,await 之后的代码会异步执行
import thenFs from 'then-fs'

console.log('A')

async function getAllFile() {
    console.log('B')
    const r1 = await thenFs.readFile('./files/1.txt', 'utf8')
    console.log(r1)
    const r2 = await thenFs.readFile('./files/2.txt', 'utf8')
    console.log(r2)
    const r3 = await thenFs.readFile('./files/3.txt', 'utf8')
    console.log(r3)
    console.log('D')
}

getAllFile()
console.log('C')

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

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

相关文章

手把手实现邮件分类 《Getting Started with NLP》chap2:Your first NLP example

《Getting Started with NLP》chap2:Your first NLP example 感觉这本书很适合我这种菜菜,另外下面的笔记还有学习英语的目的,故大多数用英文摘录或总结 文章目录《Getting Started with NLP》chap2:Your first NLP example2.1 Introducing N…

数据结构与算法【树】

二叉树性质 满二叉树 深度为k,有2k−12^{k}-12k−1个结点的二叉树,为满二叉树。 完全二叉树 完全二叉树的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面…

CSDN第22期周赛(记录一下,不是题解)

希望23年能收获一两本程序员杂志 前言 发现一个问题,codeblocks上编译没问题,在CSDN比赛时,会报错: 1,size()和length()属于unsigned int,所以与之比较大小或者赋值的 i, j 也要用unsigned int&#xf…

巧解 JavaScript 中的嵌套替换

网友 wys 提问&#xff1a;如何仅使用 JavaScript 支持的正则语法&#xff0c;将 <p> <table> <p> <p> </table> <table> <p> <p> </table> <p>中<table>...</table>之间的<p>都替换为<b…

C库函数:stdio.h

stdio.h C 标准库 – <stdio.h> | 菜鸟教程 (runoob.com) 下面是头文件 stdio.h 中定义的变量类型&#xff1a; 序号变量 & 描述1size_t 这是无符号整数类型&#xff0c;它是 sizeof 关键字的结果。2FILE 这是一个适合存储文件流信息的对象类型。3fpos_t 这是一个适…

组件的生命周期

一、组件的生命周期 1、组件的生命周期&#xff1a;至一个组件从 创建——>运行——>销毁的过程 2、声明周期函数&#xff1a;由Vue提供的内置函数&#xff0c;伴随组件生命周期按次序自动运行——>钩子函数 3、生命周期的阶段划分 &#xff08;1&#xff09;创建…

什么是链接?(动态链接库和静态链接库的对比)

什么是链接&#xff1f; 首先我们需要知道&#xff0c;一个源文件&#xff08;以.c为例&#xff09;是经过什么最后形成的一个可执行的文件&#xff08;windows下为.exe文件&#xff09;。 一个.c的源文件&#xff0c;要经历 1.预处理&#xff1a;头文件的展开替换 2.编译&…

skywalking解析-如何在idea中调试skywalking agent

当我从github上下载下来skywalking agent的代码后&#xff0c;面临的第一个问题就是如何调试。因为skywalking agent的运行模式与普通程序运行方式不一样&#xff0c;它是通过java agent方式运行的。本文接下来介绍如何在本地调试skywalking agent源码。 目录一、下载源码二、运…

leetcode_栈与队列

栈与队列栈与队列理论基础232.用栈实现队列225.用队列实现栈20.有效的括号1047.删除字符串中的所有相邻重复项150.逆波兰表达式求值239.滑动窗口最大值347.前k个高频元素栈与队列总结栈与队列理论基础 栈与队列理论基础 232.用栈实现队列 力扣题目链接 class MyQueue { pub…

Cadence PCB仿真使用Allegro PCB SI通过导入工艺文件配置层叠结构的方法图文教程

⏪《上一篇》   🏡《总目录》   ⏩《下一篇》 目录 1,概述2,配置方法3,总结1,概述 本文简单介绍使用Allegro PCB SI通过导入工艺文件配置层叠结构的方法。 2,配置方法 第1步:打开待仿真的PCB文件,并确认软件为Allegro PCB SI 如果,打开软件不是Allegro PCB SI则…

【JavaScript】数组常用方法

冲突数组常用方法&#xff1a; 注&#xff1a; 以下方法都会对原数组进行改变&#xff1a; push&#xff1a;向数组后面追加元素&#xff0c;返回值是追加后的数组长度 pop&#xff1a;从数组后面删除元素&#xff0c;返回值是删除的元素内容 unshift:在数组前面添加元素&am…

CMMI之系统设计

系统设计&#xff08;System Design, SD&#xff09;是指设计软件系统的体系结构、用户界面、数据库、模块等&#xff0c;从而在需求与代码之间建立桥梁&#xff0c;指导开发人员去实现能满足用户需求的软件产品。系统设计过程域是SPP模型的重要组成部分。本规范阐述了系统设计…

第一章 Flink简介

Flink 系列教程传送门 第一章 Flink 简介 第二章 Flink 环境部署 第三章 Flink DataStream API 第四章 Flink 窗口和水位线 第五章 Flink Table API&SQL 第六章 新闻热搜实时分析系统 前言 流计算产品实时性有两个非常重要的实时性设计因素&#xff0c;一个是待计算…

文档智能(一):基于OpenCV的文档图像校正

文档智能(一)&#xff1a;基于OpenCV的文档图像校正 发表时间&#xff1a; 2023年1月7日创作地点&#xff1a;湖北省武汉市作者&#xff1a;ixy_com&[Aneerban Chakraborty]封面图片来源&#xff1a;DocTr 本文关键词&#xff1a;文档智能、文档图像校正、OpenCV、形态…

从零实现Dooring低代码印章组件

上一篇文章和大家分享了低代码平台组件间通信方案的几种实现:低代码平台组件间通信方案复盘今天继续和大家分享一下比较有意思的可视化印章组件的实现.你将收获低代码组件的基本设计模式印章组件的设计原理(canvas相关)如何快速将任意组件集成到低代码平台正文低代码组件的基本…

雷鸟X2:开启可量产全彩MicroLED光波导AR眼镜新起点

从最近的AR眼镜新品来看&#xff0c;采用MicroLED光波导方案已经成为了明显的趋势&#xff0c;可见业内对于光学的大方向还是非常统一的。不仅如此&#xff0c;各个厂商都拿出自己最优的方案来进行探索和验证&#xff0c;比如有的看重“极轻”、有的看重“视觉”、有的看重“价…

使用Jenkins一键打包部署 SpringBoot应用

一般而言&#xff0c;一个项目部署的由&#xff1a;拉取代码->构建->测试->打包->部署等过程组成&#xff0c;如果我们经常需要部署项目&#xff0c;特别是在微服务时代&#xff0c;服务特别多的情况下&#xff0c;不停的测试打包部署&#xff0c;那估计得有个人一…

数学:一夜读罢头飞雪

文章目录引子代数&#xff0c;几何与分析数学之美微积分形式的统一之美伽罗华群论的深刻之美几何的形体之美公理与定理集合论的公理欧几里得几何公理算术公理实数系的公理系统数学攀登的路径数学的符号系统希腊字母表物理与数学推荐的数学读物参考链接引子 贺新郎读史 人猿相揖…

【阶段二】Python数据分析数据可视化工具使用05篇:统计直方图、面积图与箱型图

本篇的思维导图: 统计直方图 统计直方图(histogram)形状类似柱形图,却有着与柱形图完全不同的含义。统计直方图涉及统计学的概念,首先要从数据中找出它的最大值和最小值,然后确定一个区间,使其包含全部测量数据,将区间分成若干个小区间,统计测量结果出现在各…

详细讲解Linux PCI驱动框架分析

说明&#xff1a; Kernel版本&#xff1a;4.14 ARM64处理器 使用工具&#xff1a;Source Insight 3.5&#xff0c; Visio 1. 概述 从本文开始&#xff0c;将会针对PCIe专题来展开&#xff0c;涉及的内容包括&#xff1a; PCI/PCIe总线硬件&#xff1b; Linux PCI驱动核心框…