为了解决回调地狱的问题,ES6(ECMAScript 2015)中新增了 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() 方法时,成功的回调函数是必选的、失败的回调函数是可选的。基于以上的理论,我们来看Promise的常见用法。
基于回调函数按顺序读取文件内容,具体代码如下:
//读取文件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) => { //诗取文件 2 失败
if (err2) return console.log(err2.message)
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 成功
})
})
})
基于 then-fs 读取文件内容
由于 node.js 官方提供的 fs 模块仅支持以回调函数的方式读取文件,不支持 Promise 的调用方式。因此,需要先运行如下的命令,安装 then-fs 这个第三方包,从而支持我们基于 Promise 的方式读取文件的内容。
npm install then-fs
调用then-fs提供的readFile()方法,可以异步地读取文件的内容,C的返回值是Promise的实例对象。因此可以调用.then()方法为每个Promise异步操作指定成功和失败之后的回调函数。示例代码如下:
then-fs 的基本使用
调用 then-fs 提供的 readFile() 方法,可以异步地读取文件的内容,它的返回值是 Promise 的实例对象。因此可以调用 .then() 方法为每个 Promise 异步操作指定成功和失败之后的回调函数。示例代码如下:
/★★
*基于Promise的方式读取文件
*/
impoik: 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)})
注意:上述的代码无法保证文件的读取顺序,需要做进一步的改进!
.then() 方法的特性
如果上一个 .then() 方法中返回了一个新的 Promise 实例对象,则可以通过下一个 .then() 继续进行处理。通过 .then() 方法的链式调用,就解决了回调地狱的问题。
基于 Promise 按顺序读取文件的内容
Promise 支持链式调用,从而来解决回调地狱的问题。示例代码如下:
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)
})
通过 .catch 捕获错误
在 Promise 的链式操作中如果发生了错误,可以使用 Promise.prototype.catch 方法进行捕获和处理:
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 =>{//捕获第1行发生的错误,并输出错误的消息
console.log(err.message)
})
Promise.all() 方法
Promise.all() 方法会发起并行的 Promise 异步操作,等所有的异步操作全部结束后才会执行下一步的 .then操作(等待机制)。示例代码如下:
//1.定义一个数组,存放3个读文件的异步操作
const promiseArr = [
thets.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)
})
注意:数组中 Promise 实例的顺序,就是最终结果的顺序!
Promise.race() 方法
Promise.race() 方法会发起并行的 Promise 异步操作,只要任何一个异步操作完成,就立即执行下一步的 .then 操作(赛跑机制)。示例代码如下:
//1.定义一个数组,存放3个读文件的异步操作
const promiseArr =[
thenFs.readFile('./files/1.txt', 'utf8'),
thenFs.readFile('./files/2.txt', 'utf8').
thenFs.readFile('./files/3.txt', 'utf8').
]
// 2.将 Promise 的数组,作为 Promise.race()的参数
Pronise.race(promiseArr)
.then((result)=>{//2.1只要任何一个异步操作完成,就立即执行成功的回调函数(赛跑机制)
console.log(result)
})
.catch(err =>{// 2.2捕获Promise 异步操作中的错误
console.log(err.message)
})