从零手撕Promise,掌握Promise的实现原理(3)之回调地狱是什么

397 阅读2分钟

「这是我参与11月更文挑战的第3天,活动详情查看:2021最后一次更文挑战」。

建议阅读前两篇文章

从零手撕Promise,掌握Promise的实现原理。(1)

从零手撕Promise,掌握Promise的实现原理。(2)

回顾

通过上一篇文章的介绍,我们已经完成了基础版本的promise,如下

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class Promise{
  constructor(executor){

    this.state = PENDING
    this.value = undefined
    this.reason = undefined
    //存放onFulfilled
    this.onResolvedCallbacks = []
    //存放onRejected
    this.onRejectedCallbacks = []
    const resolve = (value) => {
      if (this.state === PENDING) {
        this.value = value
        this.state = FULFILLED
        //promise实例状态改变后调用暂存的onFulfilled
        this.onResolvedCallbacks.forEach(fn => fn())
      }
    }

    const reject = (reason) => {
      if (this.state === PENDING) {
        this.reason = reason
        this.state = REJECTED
        //promise实例状态改变后调用的onRejected
        this.onRejectedCallbacks.forEach(fn => fn())
      }
    }
    try {
      //executor函数执行过程中出错,将会导致Promise失败
      executor(resolve,reject)
    } catch (error) {
      reject(error)
    }
  }
  then(onFulfilled, onRejected){
    if (this.state === FULFILLED) {
      onFulfilled(this.value)
    }

    if (this.state === REJECTED) {
      onRejected(this.reason)
    }

    if (this.state === PENDING) {
      //如果此时promise实例的状态还未确定,我们需要将onFulfilled与onRejected存起来,等到promise实例状态改变后再去调用
      this.onResolvedCallbacks.push(() => {
        onFulfilled(this.value)
      })
      this.onRejectedCallbacks.push(() => {
        onRejected(this.reason)
      })
    }

  }
}

回调地狱

在实现Promisethen链调用机制之前我们要先来了解一下回调地狱,以及用Promise如何去解决回调地狱.

  • 有两个文本文件age.txtname.txt,内容分别如下。

agetxt.png nametxt.png

  • 我需要读取到age.txt中的路径再用这个路径去读取name.txt的内容,代码如下。
    const fs = require('fs') //导入node中的文件模块
    fs.readFile('./age.txt', 'utf8', function(err, data){
      if(err) throw new Error(err)
      fs.readFile(data,'utf8',function (err,data) {
          if(err) throw new Error(err)
          console.log(data)
      })
    })
    
  • 假如有多个文件这样嵌套呢,那我们的代码就会变成这样,也就是我们所说的回调地狱。
    fs.readFile('./age.txt', 'utf8', function(err, data){
      if(err) throw new Error(err)
      fs.readFile(data,'utf8',function (err,data) {
          if(err) throw new Error(err)
          fs.readFile(data,'utf8',function (err,data) {
              if(err) throw new Error(err)
              fs.readFile(data,'utf8',function (err,data) {
                  if(err) throw new Error(err)
                  fs.readFile(data,'utf8',function (err,data) {
                    if(err) throw new Error(err)
                    console.log(data)
                })
              })
          })
      })
    })
    
  • 那么Promise作为一种异步编程的解决方案,它是如何解决回调地狱这种情况的呢,我们来看代码。
    • 首先我们将readFile的方法封装成promise
    const readFile = (filePath) => {
        return new Promise((resolve,reject)=>{
             fs.readFile(filePath,'utf8',function (err,data) {
                 if(err) {// 失败了调用reject
                     return reject(err);
                 } 
                 resolve(data); // 成功调用resolve
             })
        })
    }
    
    • 用我们封装后的readFile再去读取age.txt,name.txt的内容,编写代码就很友好了。
    readFile('./age.txt').then(data => {
      return readFile(data)
    }).then(data => {
      console.log(data)
    },reason => {
      console.log(reason)
    })
    
    • 如愿以偿得到了我们想要的结果 11-04md.png

这篇文章主要用来介绍回调地狱,下篇文章将会继续完成Promise.