重学Promise

86 阅读4分钟

附:「这是我参与2022首次更文挑战的第2天,活动详情查看:2022首次更文挑战

本文示例代码已提交到 github:github.com/hec990/Prom…

基本概念

1. Promise 是一个构造函数

  • 是构造函数则我们可以使用 new 创建 Promise 的实例 ==> const p = new Promise()
  • new 出来的 Promise 实例对象,代表一个异步操作

2. Promise.prototype 上有一个 .then() 方法

  • console.dir(Promise) 查看Promise对象上的属性
  • 第一次 new Promise() 构造函数得到的实例对象
  • 都可以 通过原型链的方式 访问到 .then 方法,例如:p.then

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

  • p.then(成功的回调函数,失败的回调函数)
  • 例如:p.then(res =>{} , err =>{})
  • 调用 .then() 方法时,成功的回调函数是必选的,失败的回调函数是可选的(如果你觉得不会报错,就可以只传一个成功回调即可)

读取文件

回调地狱

我们要想依次按顺序读取 1.txt 、2.txt、3.txt文件,就必然会用到回调,但是这样会导致回调地狱的问题

使用 Promise 解决回调地狱的问题

基于 then-fs 读取文件内容

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

yarn add then-fs

&&

npm install then-fs

(2)基本使用

  • 先引入 then-fs 模块
  • 然后调用 then-fs 提供的 readFile() 方法
  • 这样我们就可以异步地读取文件的内容了,它的返回值是 Promise 的实例对象。因此可以调用 .then() 方法为每个 Promise 异步操作指定成功和失败之后的回调函数,示例代码如下:
import thenFs from 'then-fs'

thenFs.readFile('./files/1.txt','utf-8').then(rs =>{console.log(rs)})
thenFs.readFile('./files/2.txt','utf-8').then(rs =>{console.log(rs)})
thenFs.readFile('./files/3.txt','utf-8').then(rs =>{console.log(rs)})

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

输出结果:

解决无法保证读取顺序问题

补充:.then() 方法的特性

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

thenFs.readFile('./files/1.txt','utf-8')
    .then(rs1 =>{
        console.log(rs1)
        return thenFs.readFile('./files/2.txt','utf-8') // 返回了一个新的Promise实例对象,后面可以使用 .then 拿结果
    })
    .then(rs2 =>{
        console.log(rs2)
        return thenFs.readFile('./files/3.txt','utf-8') // 返回了一个新的Promise实例对象,后面可以使用 .then 拿结果
    })
    .then(rs3 =>{
        console.log(rs3)
    })

输出结果:按顺序依次输出,这里重复执行了3次,都是同样结果,说明这样写是没有问题的。

通过 .catch 捕获错误

需要注意有两种情况捕获错误,一种是放最后,一种是放在最前

  • 放最后:如果代码在中间有错误,代码不会继续执行,会直接跳到 .catch
  • 放最前:如果不希望代码报错影响后面代码执行结果,可以放在最前

(1)放最后

import thenFs from 'then-fs'

thenFs.readFile('./files/1.txt','utf-8')
    .then(rs1 =>{
        console.log(rs1)
        return thenFs.readFile('./files/22.txt','utf-8') // 故意将 2.txt 改为 22.txt(一个不存在的文件)
    })
    .then(rs2 =>{
        console.log(rs2)
        return thenFs.readFile('./files/3.txt','utf-8')
    })
    .then(rs3 =>{
        console.log(rs3)
    })
    .catch(err =>{
        console.log(err.message)
    })

输出结果:代码并没有往下执行,而是直接跳转到捕获错误

(2)放最前

import thenFs from 'then-fs'

thenFs.readFile('./files/11.txt','utf-8') // 故意将 1.txt 改为 11.txt(一个不存在的文件)
    .catch(err =>{
        console.log(err.message)
    })
    .then(rs1 =>{
        console.log(rs1) ) // 输出undefined
        return thenFs.readFile('./files/2.txt','utf-8') 
    })
    .then(rs2 =>{
        console.log(rs2) // 222
        return thenFs.readFile('./files/3.txt','utf-8')
    })
    .then(rs3 =>{
        console.log(rs3) // 333
    })

输出结果:提前捕获错误,不影响后续代码执行

Promise的其他使用方法

Promise.all

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

import thenFs from 'then-fs'

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

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

输出结果:

注意点:定义数组的顺序,就是最终结果的顺序

Promise.race

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

import thenFs from 'then-fs'

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

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

输出结果:每次的结果都不一样,谁先请求完就先执行谁

总结

  1. Promise.all 和 Promise.race 的区别
  • Promise.all 等待所有异步操作执行完才执行下一步的 .then 操作
  • Promise.race 是谁先执行完,谁先执行下一步的 .then 操作