回调地狱
多层回调函数的相互嵌套,就形成了回调地狱。示例代码如下: ```javascript setTimeout(() => { console.log(‘延迟 1 秒后输出’)
setTimeout(() => {
console.log('延迟 2 秒后输出')
setTimeout(() => {
console.log('延迟 3 秒后输出')
},3000)
}, 2000)
}, 1000)
- 缺点:- 代码耦合性太强,牵一发而动全身,难以维护- 大量冗余的代码相互嵌套,代码的可读性变差<a name="SHeok"></a>## Promise 基本概念1. Promise 是一个构造函数- 可以创建 Promise 的实例对象```javascriptconst p = new Promise()
- new 出来的 Promise 实例对象,代表一个异步操作
- Promise.prototype 上包含一个 .then() 方法
- 每一次 new Promise() 构造函数得到的实例对象
- 都可以通过原型链的方式访问到 .then() 方法,例如 p.then()
- .then() 方法用来预先指定成功和失败的回调函数
- p.then( 成功的回调函数 , 失败的回调函数 )
- p.then( result => { } , error => { } )
- 调用 .then() 方法时,成功的回调函数是必选的、失败的回调函数是可选的
基于回调函数按顺序读取文件内容
const fs = require('fs');// 读取文件 1.txtfs.readFile('./files/1.txt', 'utf8', (err1, r1) => {if(err1) return console.log(err1.message) // 读取文件 1 失败console.log(r1) // 读取文件 1 成功// 读取文件 2.txtfs.readFile('./files/2.txt', 'utf8', (err2, r2) => {if(err2) return console.log(err2.message) // 读取文件 2 失败console.log(r2) // 读取文件 2 成功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 的方式读取文件的内容:
调用 then-fs 提供的 readFile() 方法,可以异步地读取文件的内容,它的返回值是 Promise 的实例对象
- 因此可以调用 .then() 方法为每个 Promise 异步操作指定成功和失败后的回调函数,示例如下:
- 读取文件的顺序不正确
// 基于 Promise 的方式读取文件import thenFs from 'then-fs'// 注意 .then() 中的失败回调时可选的,可以被省略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) })
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);
thenFs.readFile('./files/3.txt', 'utf8').then((r3) => {console.log(r3);})
}) })
<a name="Dompv"></a># Promise.all()- Promise.all() 方法会发起并行的 Promise 异步操作,等所有的异步操作全部结束后才会执行下一步的 .then 操作(等待机制),示例代码如下:```javascriptimport thenFs from "then-fs";// 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.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.race()
- Promise.race() 方法会发起并行的 Promise 异步操作,只要任何一个异步操作完成,就立即执行下一步的 .then() 操作(赛跑机制),示例代码如下:
import thenFs from "then-fs";// 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.all() 的参数Promise.race(promiseArr).then((result) => { // 2.1 只要任何一个异步操作完成,就立即执行成功的回调函数(赛跑机制)console.log(result);}).catch(err => { // 2.2 捕获 promise 异步操作中的错误console.log(err.message);})
Promise 封装读文件方法
- 方法的名称要定义为 getFile
- 方法接收一个形参 fpath,表示要读取的文件的路径
- 方法的返回值为一个 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); })
<a name="uDdCU"></a># async/await- async/await 是 ES8 引入的新语法- 简化 Promise 异步操作- 在它出现之前开发者只能通过链式 .then() 的方式处理 Promise 异步操作- .then 链式调用的优点:解决了回调地狱的问题<a name="DENeM"></a>## 基本使用使用 async/await 简化 Promise 异步操作的示例代码如下:```javascriptimport thenFs from "then-fs"// 按序读取文件 1, 2, 3 的内容async function getAllFile() {const r1 = await thenFs.readFile('./files/1.txt', 'utf8')console.log(r1);const r2 = await thenFs.readFile('./files/2.txt', 'utf8')console.log(r2);const r3 = await thenFs.readFile('./files/3.txt', 'utf8')console.log(r3);}getAllFile()
注意事项
- 如果在 function 中使用了 await,则 function 必须被 async 修饰
- 在 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 ```
