Promise 异步用法

一、回调地狱

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

回调地狱的缺点:

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

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

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

二、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 基于回调函数按顺序读取文件内容

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(err1) return console.log(err2.message) // 读取 文件2 失败
      console.log(r2)// 读取 文件1 成功

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


// 结果:
// txt file 1
// txt file 2
// txt file 3

2.2 基于 then-fs 读取文件内容

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

npm install then-fs

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

import thenFs from 'then-fs'
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)})

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

.then() 方法的特性

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

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

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

import thenFs from 'then-fs'

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

// 运行结果:
// txt file 1
// txt file 2
// txt file 3

2.4 通过 .catch 捕获错误

在 Promise 的链式操作中如果发生了错误,可以使用 Promise.prototype.catch 方法进行捕获和处理:

import thenFs from 'then-fs'

thenFs.readFile("./files/10.txt",'utf8') // 文件.10txt 不存在导致读取失败,后面的 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)
})

// 运行结果:
// ENOENT: no such file or directory, open 'E:\study\es6\files\10.txt'

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

import thenFs from 'then-fs'

thenFs.readFile("./files/10.txt",'utf8') // 文件.10txt 不存在导致读取失败,后面的 3 个 .then 都不执行。
.catch(err=>{   // 捕获第一行发送的错误,并输出错误消息 
  console.log(err.message)
})
.then(r1=>{     // 由于 错误已经被及时处理,不影响后续 .then 的正常执行。
  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)
})

// 运行结果:
// undefined
// txt file 2
// txt file 3

 2.5 Promise.all() 方法

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

import thenFs from 'then-fs'

// 定义一个数组,存放 3个文件读取的异步操作。
const promiseAll = [
  thenFs.readFile("./files/1.txt",'utf8'),
  thenFs.readFile("./files/2.txt",'utf8'),
  thenFs.readFile("./files/3.txt",'utf8')
]
// 将 Promise 的数组,作为 Promise.all() 的参数
Promise.all(promiseAll)
.then(([r1,r2,r3])=>{   // 所有文件读取成功(等待机制)
  console.log(r1,r2,r3)
})
.catch(err=>{  // 捕获 promise 异步操作中的 错误
  console.log(err.message)
})

// 注意:数组中 Promise 实例的顺序,就是最终结果的顺序!
// 运行结果:
// txt file 1
// txt file 2
// txt file 3

2.6 Promise.race() 方法

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

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

// 将 Promise 的数组,作为 Promise.race() 的参数
Promise.race(promiseAll)
.then((result)=>{ // 只要任何一个异步操作完成,就立即执行下一步的 .then 操作(赛跑机制)。
  console.log(result)
})
.catch(err=>{  // 捕获 promise 异步操作中的 错误
  console.log(err.message)
})

// 运行结果1:
// txt file 1

// 运行结果2:
// txt file 3

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

方法的封装要求:

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

import fs from "fs"

function getFile(fpath){
  // 方法的返回值为  Promise 的实例对象
  // resolve : 调用 fetFiles() 方法时 通过 .then 指定‘成功的’回调函数。
  // reject  : 调用 fetFiles() 方法时 通过 .then 指定‘失败的’回调函数。
  return new Promise(function(resove,reject){
    fs.readFile(fpath,'utf8',(err,dataStr)=>{
      if(err) return reject(err)
      resove(dataStr)
    })
  })
}

// getFile 方法的调用过程:
// getFile('./files/1.txt').then(成功的回调函数,失败的回调函数)
getFile('./files/1.txt').then((r1)=>{console.log(r1)},(error)=>{console.log(error.message)})
//也可以使用 catch 捕获失败信息
getFile('./files/1.txt').then((r1)=>{console.log(r1)}).catch((error)=>{console.log(error.message)})

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值