回调地狱
多层回调函数的相互嵌套,就形成了回调地狱。示例代码如下: ```javascript setTimeout(() => { console.log(‘延迟 1 秒后输出’)
setTimeout(() => {
console.log('延迟 2 秒后输出')
setTimeout(() => {
console.log('延迟 3 秒后输出')
},3000)
}, 2000)
}, 1000)
- 缺点:
- 代码耦合性太强,牵一发而动全身,难以维护
- 大量冗余的代码相互嵌套,代码的可读性变差
<a name="SHeok"></a>
## Promise 基本概念
1. Promise 是一个构造函数
- 可以创建 Promise 的实例对象
```javascript
const 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.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 成功
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 操作(等待机制),示例代码如下:
```javascript
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.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 异步操作的示例代码如下:
```javascript
import 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 ```