目录
多层回调函数的相互嵌套,就形成了回调地狱。示例如下:
回调地狱的缺点:
代码耦合性太强,牵一发而动全身,难以维护
大量冗余的代码相互嵌套,代码的可读性变差
为了解决回调地狱的问题,ES6(ECMAScript 2015)中新增了 Promise 的概念。
① Promise 是一个构造函数
我们可以创建 Promise 的实例 const p = new Promise()
new 出来的 Promise 实例对象,代表一个异步操作
② Promise.prototype 上包含一个 .then() 方法
每一次 new Promise() 构造函数得到的实例对象,
都可以通过原型链的方式访问到 .then() 方法,例如 p.then()
③ .then() 方法用来预先指定成功和失败的回调函数
p.then(成功的回调函数,失败的回调函数)
p.then(result => { }, error => { })
调用 .then() 方法时,成功的回调函数是必选的、失败的回调函数是可选的
一层套一层,和1.回调地狱一样,这里就不做演示了。。。
由于 node.js 官方提供的 fs 模块仅支持以回调函数的方式读取文件,不支持 Promise 的调用方式。因此,需
要先运行如下的命令,安装 then-fs 这个第三方包,从而支持我们基于 Promise 的方式读取文件的内容:
npm install 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() 方法中返回了一个新的 Promise 实例对象,则可以通过下一个 .then() 继续进行处理。通
过 .then() 方法的链式调用,就解决了回调地狱的问题。
Promise 支持链式调用,从而来解决回调地狱的问题。示例代码如下:
- import thenFs from 'then-fs'
-
- thenFs
- .readFile('./files/11.txt', 'utf8') //返回值是 Promise 的实例对象
- .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)
- })
在 Promise 的链式操作中如果发生了错误,可以使用 Promise.prototype.catch 方法进行捕获和处理:
- import thenFs from 'then-fs'
-
- thenFs.readFile('./files/1.txt','utf8').then((r1)=>{
- console.log(r1)
- return thenFs.readFile('./files/22.txt','utf8')
- .catch( err =>{ //通过 catch 捕获错误。但是从这里往后就不会捕获了。
- console.log(err)
- })
- }).then((r2)=>{
- console.log(r2)
- return thenFs.readFile('./files/3.txt','utf8')
- }).then((r3)=>{
- console.log(r3)
- })
-
- //运行结果是 读取出 r1 r2打印undefined r3
- //如果把 catch 放到最后,则遇到错误以后爆错误,错误后面就不会再执行了
- //在这里把 catch 放到最后,就能读取出 r1 ,然后就爆err了,r2 ,r3 都不会读取出来
如果不希望前面的错误导致后续的 .then 无法正常执行,则可以将 .catch 的调用提前
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(([r1,r2,r3])=>{
- console.log(r1,r2,r3)
- })
- .catch(err=>{
- console.log(err)
- })
注意:数组中 Promise 实例的顺序,就是最终结果的顺序!
Promise.race() 方法会发起并行的 Promise 异步操作,只要任何一个异步操作完成,就立即执行下一步的
.then 操作(赛跑机制)。
示例代码如下:
- import thenFs from 'then-fs'
-
- const promiseArr = [
- thenFs.readFile('./files/3.txt', 'utf8'),
- thenFs.readFile('./files/2.txt', 'utf8'),
- thenFs.readFile('./files/1.txt', 'utf8'),
- ]
-
- Promise.race(promiseArr).then(result => {
- console.log(result)
- })
-
- //最后就打印出一个,哪个读取快就打印哪个,读取出一个就立马停止后面的读取。
- //有可能出r1 ,有可能出r3,不固定,谁快读取谁!
方法的封装要求:
① 方法的名称要定义为 getFile
② 方法接收一个形参 fpath,表示要读取的文件的路径
③ 方法的返回值为 Promise 实例对象
- // 1.方法的名称为 getFile
- // 2.方法接收一个形参 fpath, 表示要读取的文件的路径
- function getFile(fpath) {
- //3.方法的返回值为 Promise 的实例对象
- return new Promise()
- }
注意:第 5 行代码中的 new Promise() 只是创建了一个形式上的异步操作。
如果想要创建具体的异步操作,则需要在 new Promise() 构造函数期间,传递一个 function 函数,将具体的
异步操作定义到 function 函数内部。
通过 .then() 指定的成功和失败的回调函数,可以在 function 的形参中进行接收。
Promise 异步操作的结果,可以调用 resolve 或 reject 回调函数进行处理。
示例代码如下:
- import fs from 'fs'
-
- function getFile(fpath) {
- return new Promise(function (resolve, reject) {
- fs.readFile(fpath, 'utf8', (err, dataStr) => {
- if (err) return reject(err)
- resolve(dataStr)
- })
- })
- }
-
- getFile('./files/11.txt')
- .then((r1) => {
- console.log(r1)
- })
- .catch((err) => console.log(err.message))
-
星月前端博客https://xingyue.vercel.app/
个人博客,记录前端学习笔记,欢迎收藏或者提意见。