1. 回调地狱
   
    多层回调函数的相互嵌套,就形成了回调地狱。
    
    如下:
   
setTimeout(() => {//第一层回调函数
  console.log('延时1秒后输出')
  setTimeout(() => {//第二层回调函数
    console.log('再延时2秒后输出')
    setTimeout(() => {//第三层回调函数
      console.log('再延时3秒后输出')
    },3000)
  },2000)
},1000)
不断地嵌套,不断地缩进,使代码难以阅读、难以维护。
    
    
    1.1解决回调地狱的问题
   
为了解决回调地狱的问题,ES6(ECMAScript2015)中新增了Promise的概念。
    
    
    1.2Promise的基本概念
   
    ①Promise是一个构造函数
    
    ● 可以使用new关键词创建Promise实例
    
    ● new出来的Promise实例对象,代表一个异步操作
    
    ②Promise.prototype原型对象上包含一个 .then()方法
    
    每一次new Promise()构造函数得到的实例对象,都可以通过原型链的方式访问到.then()方法,例如p.then()
    
    ③.then()方法用来预先指定成功和失败的回调函数
    
    ● p.then(成功的回调函数,失败的回调函数)
    
    ● p.then(result => {},error => {})
    
    ● 调用.then()方法时,成功的回调函数是必选的、失败的回调函数是可选的
   
    
    
    2. 基于回调函数按顺序读取文件内容
   
     
   
按照顺序读取文件夹?中的文件
//读取文件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 (err2) return console.log(err2.message) //读取文件2失败
   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成功
    })
  })
})
为保证读取的顺序,读取文件的函数往内又嵌套了两层,但是同时也形成了回调地狱。
    
    
    3. 基于then-fs读取文件内容
   
由于node.js官方提供的fs模块仅支持以回调函数的方式读取文件,不支持 Promise 的调用方式。因此,需要先运行如下的命令,安装 then-fs这个第三方包,从而支持我们基于Promise的方式读取文件的内容:
npm install then-fs
    
    
    3.1. then-fs的基本使用
   
    调用then-fs提供的readFile()方法,可以异步地读取文件的内容,它的返回值是Promise的实例对象。因此可以调用.then()方法为每个Promise异步操作指定成功和失败之后的回调函数。
    
    如下所示:
   
import thenfs from 'then-fs'
thenfs.readFile('./files/1.txt', 'utf8').then((r1) => {
    console.log(r1)
})
thenfs.readFile('./files/2.txt', 'utf8').then((r2) => {
    console.log(r2)
})
thenfs.readFile('./files/3.txt', 'utf8').then((r3) => {
    console.log(r3)
})
    通过then-fs提供的readFile()方法可以读取文件,但是无法保证文件的读取顺序,还需要进一步改造。
    
     
   
    
    
    3.2. then()方法的特性
   
如果上一个.then()方法中返回了一个新的Promise实例对象,则可以通过下一个.then()继续进行处理。通过then()方法的链式调用,就解决了回调地狱的问题。
    
    
    3.3. 基于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)
}
    
    
    3.4. 通过.catch捕获错误
   
在Promise的链式操作中如果发生了错误,可以使用Promise.prototype.catch方法进行捕获和处理:
 import thenFs from 'then-fs'
thenFs.readFile('./files/11.txt', 'utf8')
    .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); })
如果不希望前面的错误导致后续的.then无法正常执行,则可以将.catch的调用提前,如下:
import thenFs from 'then-fs'
thenFs.readFile('./files/11.txt', 'utf8')
    .catch((err) => {
        console.log(err.message);
    })
    .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)
    })
    
    
    3.5. Promise.all()方法
   
Promise.all()方法会发起并进行的Promise异步操作,等所有的异步操作全部结束后才会执行下一步的.then操作(等待机制)。如下:
import thenFs from "then-fs";
const promiseArr = [
    thenFs.readFile('./files/1.txt', 'utf8'),
    thenFs.readFile('./files/2.txt', 'utf8'),
    thenFs.readFile('./files/3.txt', 'utf8')
]
Promise.all(promiseArr).then(result => {
    console.log(result);
})
    
    
    3.6. Promise.race()方法
   
Promise.race()方法会发起并行的Promise异步操作,只要任何一个异步操作完成,就立即执行下一步的.then操作(赛跑机制)。如下:
import thenFs from "then-fs";
const promiseArr = [
    thenFs.readFile('./files/1.txt', 'utf8'),
    thenFs.readFile('./files/2.txt', 'utf8'),
    thenFs.readFile('./files/3.txt', 'utf8')
]
Promise.race(promiseArr).then(result => {
    console.log(result);
})
    
    
    4. 基于Promise封装读文件的方法
   
    方法封装的要求:
    
    ● 方法的名称要定义为getFile
    
    ● 方法接收一个形参发fpath,表示要读取的文件路径
    
    ● 方法的返回值为Promise实例对象
   
    
    
    4.1. getFile方法的基本定义
   
function getFile(fpath){
  //方法的返回值为Promise的实例对象
  return new Promise()
}
上面代码中最后一行中的 new Promise() 只是创建了一个形式上的异步操作。
    
    
    4.2. 创建具体的异步操作
   
如果想要创建具体的异步操作,则需要在new Promise()构造函数期间,传递一个function函数,将具体的异步操作定义到function函数内部。示例代码如下:
import fs from 'fs'
function fetFile(fpath) {
    return new Promise(function() {
        fs.readFile(fpath, 'utf8', (err, dataStr) => { })
    })
}
    
    
    4.3. 获取.then的两个实参
   
通过.then()指定的成功和失败的回调函数,可以在function的形参中进行接收,代码如下:
function getFile(fpath) {
//resolve 形参是:调用 getFiles()方法时,通过 .then 指定的“成功的”回调函数
//reject形参是:调用 getFiles()方法时,通过 .then指定的“失败的”回调函数
return new Promise(function(resolve,reject){
  fs.readFile(fpath,'utf8',(err,dataStr => {})
 })
}
     
   
    
    
    4.4. 调用resolve和reject回调函数
   
promise 异步操作的结果,可以调用resolve或 reject回调函数进行处理。示例代码如下:
function getFile(fpath) {
//resolve是“成功的”回调函数; reject是“失败的”回调函数
return new Promise(function(resolve,reject){
  fs.readFile(fpath,'utf8', (err,dataStr) => {
    if(err) return reject(err) // 如果读取失败,则调用"失败的回调函数”  
    resolve(dataStr)       //如果读取成功,则调用“成功的回调函数”
  })
 )}
 
//getFile方法的调用过程:
getFile( './files/1.txt ' ).then(成功的回调函数,失败的回调函数)
 
