回调地狱

  • 多层回调函数的相互嵌套,就形成了回调地狱。示例代码如下: ```javascript setTimeout(() => { console.log(‘延迟 1 秒后输出’)

    setTimeout(() => {

    1. console.log('延迟 2 秒后输出')

    setTimeout(() => {

    1. console.log('延迟 3 秒后输出')

    },3000)

    }, 2000)

}, 1000)

  1. - 缺点:
  2. - 代码耦合性太强,牵一发而动全身,难以维护
  3. - 大量冗余的代码相互嵌套,代码的可读性变差
  4. <a name="SHeok"></a>
  5. ## Promise 基本概念
  6. 1. Promise 是一个构造函数
  7. - 可以创建 Promise 的实例对象
  8. ```javascript
  9. const p = new Promise()
  • new 出来的 Promise 实例对象,代表一个异步操作
    1. Promise.prototype 上包含一个 .then() 方法
  • 每一次 new Promise() 构造函数得到的实例对象
  • 都可以通过原型链的方式访问到 .then() 方法,例如 p.then()
    1. .then() 方法用来预先指定成功和失败的回调函数
  • p.then( 成功的回调函数 , 失败的回调函数 )
  • p.then( result => { } , error => { } )
  • 调用 .then() 方法时,成功的回调函数是必选的、失败的回调函数是可选的

基于回调函数按顺序读取文件内容

  1. const fs = require('fs');
  2. // 读取文件 1.txt
  3. fs.readFile('./files/1.txt', 'utf8', (err1, r1) => {
  4. if(err1) return console.log(err1.message) // 读取文件 1 失败
  5. console.log(r1) // 读取文件 1 成功
  6. // 读取文件 2.txt
  7. fs.readFile('./files/2.txt', 'utf8', (err2, r2) => {
  8. if(err2) return console.log(err2.message) // 读取文件 2 失败
  9. console.log(r2) // 读取文件 2 成功
  10. fs.readFile('./files/3.txt', 'utf8', (err3, r3) => {
  11. if(err3) return console.log(err3.message) // 读取文件 3 失败
  12. console.log(r3) // 读取文件 3 成功
  13. })
  14. })
  15. })

基于 then-fs 读取文件内容

  • node.js 官方提供了 fs 模块仅支持以回调函数的方式读取文件,不支持 Promise 的调用方式,所以需要先安装 then-fs 第三方包,从而支持基于 Promise 的方式读取文件的内容:

    • npm install then-fs

      then-fs 基本使用

  • 调用 then-fs 提供的 readFile() 方法,可以异步地读取文件的内容,它的返回值是 Promise 的实例对象

  • 因此可以调用 .then() 方法为每个 Promise 异步操作指定成功和失败后的回调函数,示例如下:
    • 读取文件的顺序不正确
      1. // 基于 Promise 的方式读取文件
      2. import thenFs from 'then-fs'
      3. // 注意 .then() 中的失败回调时可选的,可以被省略
      4. thenFs.readFile('./files/1.txt', 'utf8').then((r1) => {console.log(r1) })
      5. thenFs.readFile('./files/2.txt', 'utf8').then((r2) => {console.log(r2) })
      6. thenFs.readFile('./files/3.txt', 'utf8').then((r3) => {console.log(r3) })

      Promise 异步按顺序读取文件内容

      ```javascript 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);

  1. thenFs.readFile('./files/3.txt', 'utf8').then((r3) => {
  2. console.log(r3);
  3. })

}) })

  1. <a name="Dompv"></a>
  2. # Promise.all()
  3. - Promise.all() 方法会发起并行的 Promise 异步操作,等所有的异步操作全部结束后才会执行下一步的 .then 操作(等待机制),示例代码如下:
  4. ```javascript
  5. import thenFs from "then-fs";
  6. // 1. 定义一个数组,存放 3 个读文件的异步操作
  7. const promiseArr = [
  8. thenFs.readFile('./files/1.txt', 'utf8'),
  9. thenFs.readFile('./files/2.txt', 'utf8'),
  10. thenFs.readFile('./files/3.txt', 'utf8')
  11. ]
  12. // 2. 将 Promise 的数组,作为 Promise.all() 的参数
  13. Promise.all(promiseArr).then(([r1, r2, r3]) => { // 2.1 所有文件读取成功(等待机制)
  14. console.log(r1, r2, r3);
  15. }).catch(err => { // 2.2 捕获 promise 异步操作中的错误
  16. console.log(err.message);
  17. })

Promise.race()

  • Promise.race() 方法会发起并行的 Promise 异步操作,只要任何一个异步操作完成,就立即执行下一步的 .then() 操作(赛跑机制),示例代码如下:
    1. import thenFs from "then-fs";
    2. // 1. 定义一个数组,存放 3 个读文件的异步操作
    3. const promiseArr = [
    4. thenFs.readFile('./files/1.txt', 'utf8'),
    5. thenFs.readFile('./files/2.txt', 'utf8'),
    6. thenFs.readFile('./files/3.txt', 'utf8')
    7. ]
    8. // 2. 将 Promise 的数组,作为 Promise.all() 的参数
    9. Promise.race(promiseArr).then((result) => { // 2.1 只要任何一个异步操作完成,就立即执行成功的回调函数(赛跑机制)
    10. console.log(result);
    11. }).catch(err => { // 2.2 捕获 promise 异步操作中的错误
    12. console.log(err.message);
    13. })

    Promise 封装读文件方法

  1. 方法的名称要定义为 getFile
  2. 方法接收一个形参 fpath,表示要读取的文件的路径
  3. 方法的返回值为一个 Promise 实例对象 ```javascript import fs from ‘fs’ function getFile(fpath) { return new Promise(function(res, rec) { fs.readFile(fpath, ‘utf8’, (err, dataStr) => { if(err) return rec(err) res(dataStr) }) }) }

getFile(‘./files/2.txt’).then((r1) => { console.log(r1); }, (err) => { console.log(err.message); })

  1. <a name="uDdCU"></a>
  2. # async/await
  3. - async/await 是 ES8 引入的新语法
  4. - 简化 Promise 异步操作
  5. - 在它出现之前开发者只能通过链式 .then() 的方式处理 Promise 异步操作
  6. - .then 链式调用的优点:解决了回调地狱的问题
  7. <a name="DENeM"></a>
  8. ## 基本使用
  9. 使用 async/await 简化 Promise 异步操作的示例代码如下:
  10. ```javascript
  11. import thenFs from "then-fs"
  12. // 按序读取文件 1, 2, 3 的内容
  13. async function getAllFile() {
  14. const r1 = await thenFs.readFile('./files/1.txt', 'utf8')
  15. console.log(r1);
  16. const r2 = await thenFs.readFile('./files/2.txt', 'utf8')
  17. console.log(r2);
  18. const r3 = await thenFs.readFile('./files/3.txt', 'utf8')
  19. console.log(r3);
  20. }
  21. getAllFile()

注意事项

  1. 如果在 function 中使用了 await,则 function 必须被 async 修饰
  2. 在 async 方法中,第一个 await 之前的代码会同步执行,await 之后的代码会异步执行 ```javascript import thenFs from “then-fs”

console.log(‘A’); async function getAllFile() { console.log(‘B’); const r1 = await thenFs.readFile(‘./files/1.txt’, ‘utf8’) const r2 = await thenFs.readFile(‘./files/2.txt’, ‘utf8’) const r3 = await thenFs.readFile(‘./files/3.txt’, ‘utf8’) console.log(r1, r2, r3); console.log(‘D’); }

getAllFile() console.log(‘C’); // 最终输出顺序为 // A // B // C // 我是大爹 // 我是二爹 // 我是小爹 // D ```