二、Promise

1、回调地狱

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

setTimeout(() => { //第 1 层回调函数
    console.log('延时 1 秒后输出')

    setTimeout(() => { //第 2 层回调函数
        console.log('再延时 2 秒后输出')

        setTimeout(() => { // 第 3 层回调函数
            console.log('再延时 3 秒后输出')

        }, 3000)
    }, 2000)
}, 1000)

在这里插入图片描述

回调地狱的缺点:

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

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

为了解决回调地狱的问题,ES6(ECMAScript 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、基于回调函数按顺序读取文件内容

  1. 在 package.json 的根节点中添加 “type”: “module” 节点
  2. 在项目根目录输入 npm init -y
  3. 在生成的package.json 根节点中添加 "type":"module",
  4. 新建flies文件夹,并在文件夹下新建1.txt2.txt3.txt三个文件,内容分别为111,222,333
  5. 新建文件02m.js并调用 node .\02m.js运行

在这里插入图片描述

在这里插入图片描述

import fs from 'fs'

//读取文件1.txt
fs.readFile('./files/1.txt', 'utf8', (err1, r1) => {
    if (err1) return console.log(err1.message) //读取文件1失败
    console.log(r1) //读取文件1成功
    //读取文件2.txt
    fs.readFile('./files/2.txt', 'utf8', (err2, r2) => {
        if (err2) return console.log(err2.message) //读取文件2失败
        console.log(r2) //读取文件2成功
        //读取文件3. txt
        fs.readFile('./files/3.txt', 'utf8', (err3, r3) => {
            if (err3) return console.log(err3.message)  //读取文件3失败
            console.log(r3) //读取文件3成功
        })
    })
})

在这里插入图片描述

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); }, err1 => { console.log(err1.message); })
thenFs.readFile('./files/2.txt', 'utf8').then(r2 => { console.log(r2); }, err2 => { console.log(err2.message); })
thenFs.readFile('./files/3.txt', 'utf8').then(r3 => { console.log(r3); }, err3 => { console.log(err3.message); })

l两次调用读取文件的顺序不同,无法保障读取顺序
两次调用读取文件的顺序不同,无法保障读取顺序

3.2 .then() 方法的特性

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

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

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

import thenFs from "then-fs";

thenFs.readFile('./files/1.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 方法进行捕获和处理:

import thenFs from "then-fs";

thenFs.readFile('./files/11.txt', 'utf8') //文件不存在导致读取失败,后面的3个 .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);
    })
    .catch(err => { // 捕获第一行发生的错误,并输出错误信息
        console.log(err.message);
    })

3.4 通过 .catch 捕获错误

如果不希望前面的错误导致后续的 .then 无法正常执行,则可以将.catch 的调用提前,示例代码如下:

import thenFs from "then-fs";

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

3.5 Promise.all() 方法

Promise.all() 方法会发起并行的 Promise 异步操作,等所有的异步操作全部结束后才会执行下一步的 .then 操作(等待机制)。示例代码如下:
注意:数组中 Promise 实例的顺序,就是最终结果的顺序!

import thenFs from "then-fs";
// 1.定义一个数组,存放三个读取文件的异步操作
const promiseArr = [
    thenFs.readFile('./files/11.txt', 'utf8'),
    thenFs.readFile('./files/2.txt', 'utf8'),
    thenFs.readFile('./files/3.txt', 'utf8'),
]

// 2.将 Promise 的数组,作为 Promise.all()的参数
Promise.all(promiseArr)
    .then(([r1, r2, r3]) => {  // 2.1 所有文件读取成功(等待机制)
        console.log(r1, r2, r3);
    })
    .catch(err => {  // 2.2 捕获 Promise 异步操作中的错误
        console.log(err.message);
    })

3.6 Promise.race() 方法

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

import thenFs from "then-fs";
// 1.定义一个数组,存放三个读取文件的异步操作
const promiseArr = [
    thenFs.readFile('./files/1.txt', 'utf8'),
    thenFs.readFile('./files/2.txt', 'utf8'),
    thenFs.readFile('./files/3.txt', 'utf8'),
]

// 2.将 Promise 的数组,作为 Promise.arce()的参数
Promise.race(promiseArr)
    .then((r) => {  // 2.1 只要任何一个异步操作完成,就立即执行成功的回调函数(赛跑机制)
        console.log(r);
    })
    .catch(err => {  // 2.2 捕获 Promise 异步操作中的错误
        console.log(err.message);
    })

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

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

4.1 getFile 方法的基本定义

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

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

在这里插入图片描述

4.2 创建具体的异步操作

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

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

4.3 获取 .then 的两个实参

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

// 1.方法的名称为 getFile
// 2.方法接收一个形参 fpath,表示要读取的文件的路径
function getFile(fpath) {
    // 3.方法的返回值是 Promise 的实例对象
        //resolve 形参是:调用getFile() 方法时,通过 .then 指定的 "成功的" 回调函数 
        //reject  形参是:调用getFile() 方法时,通过 .then 指定的 "失败的" 回调函数 
    return new Promise(function (resolve,reject) {
        // 4.下面这行代码,表示这是一个具体的、读取文件的异步操作
        fs.readFile(fpath, 'utf8', (err, dataStr) => { })
    })
}

// getFile 方法的调用过程
getFile('./files/1.txt').then(成功的回调函数,失败的回调函数)

4.4 调用 resolve 和 reject 回调函数

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

// 1.方法的名称为 getFile
// 2.方法接收一个形参 fpath,表示要读取的文件的路径
function getFile(fpath) {
    // 3.方法的返回值是 Promise 的实例对象
    //resolve 形参是:调用getFile() 方法时,通过 .then 指定的 "成功的" 回调函数 
    //reject  形参是:调用getFile() 方法时,通过 .then 指定的 "失败的" 回调函数 
    return new Promise(function (resolve, reject) {
        // 4.下面这行代码,表示这是一个具体的、读取文件的异步操作
        fs.readFile(fpath, 'utf8', (err, dataStr) => {
            if (err) return reject(err)  //如果读取失败,则调用"失败的回调函数"
            resolve(dataStr)            //如果读取成功,则调用"成功的回调函数"
        })
    })
}

// getFile 方法的调用过程
getFile('./files/1.txt').then(成功的回调函数,失败的回调函数)
import fs from 'fs'
// 1.方法的名称为 getFile
// 2.方法接收一个形参 fpath,表示要读取的文件的路径
function getFile(fpath) {
    // 3.方法的返回值是 Promise 的实例对象
    //resolve 形参是:调用getFile() 方法时,通过 .then 指定的 "成功的" 回调函数 
    //reject  形参是:调用getFile() 方法时,通过 .then 指定的 "失败的" 回调函数 
    return new Promise(function (resolve, reject) {
        // 4.下面这行代码,表示这是一个具体的、读取文件的异步操作
        fs.readFile(fpath, 'utf8', (err, dataStr) => {
            if (err) return reject(err)  //如果读取失败,则调用"失败的回调函数"
            resolve(dataStr)            //如果读取成功,则调用"成功的回调函数"
        })
    })
}

// getFile 方法的调用过程
// getFile('./files/1.txt').then(成功的回调函数,失败的回调函数)
getFile('./files/1.txt').then(r => { console.log(r); }, err => { console.log(err.message); })
getFile('./files/12.txt').then(r => { console.log(r); }, err => { console.log(err.message); })
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

瓜皮233

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值