附:「这是我参与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)
})
输出结果:每次的结果都不一样,谁先请求完就先执行谁
总结
- Promise.all 和 Promise.race 的区别
- Promise.all 等待所有异步操作执行完才执行下一步的 .then 操作
- Promise.race 是谁先执行完,谁先执行下一步的 .then 操作