1. 回调地狱
现在我有一个需求,就是依次读取a.txt,b.txt,c.txt三个文件,我利用回调嵌套的方式,示例代码如下:
注意:如果将读取三个文件的操作不进行嵌套,而是分开写,是无法保证执行顺序的。因为读取文件操作是异步的
2. Promise
Promise出现的目的是解决node.js异步编程中回调地狱的问题
- promise本身是一个构造函数,如果要使用promise去解决回调地狱的问题,先用new创建构造函数的实例对象,这个实例对象代表一个异步操作
- 调用resolve就是相当于调用then方法里面的函数
- 调用reject就相当于调用catch方法里面的函数
- 补充:.then()也可以有两个实参来指定成功和失败的回调函数,只是第2个失败的回调函数可以省略不写
2.1 现在完成之前的需求:依次读取文件
- 先定义好 p1,p2,p3.分别去读取 a.txt, b.txt, c.txt 三个文件
var fs = require('fs')
var p1 = new Promise(function(resolve, reject) {
fs.readFile('./data/a.txt', 'utf-8', function(err, data) {
if (err) {
reject(err)
} else {
resolve(data)
}
})
})
var p2 = new Promise(function(resolve, reject) {
fs.readFile('./data/b.txt', 'utf-8', function(err, data) {
if (err) {
reject(err)
} else {
resolve(data)
}
})
})
var p3 = new Promise(function(resolve, reject) {
fs.readFile('./data/c.txt', 'utf-8', function(err, data) {
if (err) {
reject(err)
} else {
resolve(data)
}
})
})
- 使用 .then 链式调用:
p1
.then(function(data) {
console.log(data)
return p2 // p2 是一个 Promise 对象
}, function(err) {
console.log('读取文件失败了', err)
})
.then(function(data) {
console.log(data)
return p3
})
.then(function(data) {
console.log(data);
console.log('end');
})
2.2改进 ------ 封装代码
可以看出 上述代码 在定义 p1,p2,p3 的时候,有很多代码重复了,我们可以进行封装。
var fs = require('fs')
// 封装的函数 pReadFile
function pReadFile(filePath) {
return new Promise(function(resolve, reject) {
fs.readFile(filePath, 'utf-8', function(err, data) {
if (err) {
reject(err)
} else {
resolve(data)
}
})
})
}
pReadFile('./data/a.txt')
.then(function(data) {
console.log(data);
return pReadFile('./data/b.txt')
})
.then(function(data) {
console.log(data);
return pReadFile('./data/c.txt')
})
.then(function(data) {
console.log(data);
console.log('end');
})
2.3 使用第三方包:基于 then-fs 读取文件内容
由于node.js 官方提供的fs模块仅支持以回调函数的方式读取文件,不支持Promise的调用方式。因此,需要先运行如下命令,安装 then-fs 这个第三方包,从而支持我们基于Promise的方式读取文件内容:
npm install then-fs
2.3.1 then-fs 的基本使用
调用then-fs 提供的readFile()方法,可以异步地读取文件的内容,它的返回值是Promise的实例对象。因此可以调用.then()方法为每个Promise异步操作指定成功和失败之后的回调函数。示例代码如下:
import thenFs from 'then-fs'
thenFs.readFile('./files/a.txt','utf-8').then((r1)=>{console.log(r1);})
thenFs.readFile('./files/b.txt','utf-8').then((r2)=>{console.log(r2);})
thenFs.readFile('./files/c.txt','utf-8').then((r3)=>{console.log(r3);})
注意:此时执行node命令不一定是按顺序的,因为就如之前提到的,读取文件操作是异步操作
2.3.2 按顺序读取文件
使用 .then()方法的链式调用
import thenFs from 'then-fs'
thenFs.readFile('./files/a.txt','utf-8')
.then((r1)=>{
console.log(r1);
return thenFs.readFile('./files/b.txt','utf-8')
})
.then((r2)=> {
console.log(r2);
return thenFs.readFile('./files/c.txt','utf-8')
})
.then((r3)=>{console.log(r3);
})
2.4 Promise.all()方法
Promise.all()方法会发起并行的Promise异步操作,等所有的异步操作全部结束后才会执行下一步的.then操作(等待机制)。示例代码如下:
import thenFs from "then-fs";
const promiseArr = [
thenFs.readFile('./files/c.txt','utf8'), // ccc
thenFs.readFile('./files/a.txt','utf8'), //aaa
thenFs.readFile('./files/b.txt','utf8') //bbb
]
Promise.all(promiseArr).then((result)=> {
console.log(result);
})
运行结果:
[ 'ccc', 'aaa', 'bbb' ]
2.5 Promise.race()方法
Promise.race()方法会发起并行的Promise异步操作,只要任何一个异步操作完成,就立刻执行下一步的.then()操作(赛跑机制)。示例代码如下:
import thenFs from "then-fs";
const promiseArr = [
thenFs.readFile('./files/a.txt','utf8'),
thenFs.readFile('./files/b.txt','utf8'),
thenFs.readFile('./files/c.txt','utf8')
]
Promise.race(promiseArr).then((result)=> {
console.log(result);
})
运行结果是不确定的,哪个文件读取得最快就打印出来了。