搞明白原理第一天,手写一个Promise

226 阅读5分钟

  • promise存在三种状态:成功(resolved),失败(rejected),进行中(pending)

  • promise的状态一旦改变就不能再改,状态只能从进行中(pending)改变到别的状态
  • promise是一个构造函数,接受一个构造器,构造器立刻执行两个函数,resolve是成功函数,返回一个成功值,reject是失败函数,返回失败原因
先实现一个普通的promise构造函数

// 先实现一个小型的promise
class Promse {
  // 接收一个执行器  两个函数 并且立马执行
  constructor(executor) {
    // 先定义初始状态  初始成功值 初始失败原因
    status = 'pending'
    value = undefined
    reason = undefined

    executor(this.resolve, this.reject)

    // 两个函数
    // 成功返回成功的值
    resolve = (value) => {
      if (this.status !== 'pending') return
      this.status = 'resolved'
      this.value = value
    }
    // 失败返回失败原因
    reject = (reason) => {
      if (this.status !== 'pending') return
      this.status = 'rejected'
      this.reason = reason
    }
  }
}

定义两个函数用于改变promise的状态,只能是从pending状态改变,其他状态直接返回

实现then方法

then方法中接受两个函数,成功和失败,当promise是pending状态下,对应执行函数

  // then方法 接受两个参数
  then (onFulfilled, onRejectedFunc) {
    if (this.status === 'pending') {
      // 然后对应状态分别函数
      if (this.status === 'resolved') {
        onFulfilled(this.value)
      }
      if (this.status === 'resolved') {
        onRejectedFunc(this.reason)
      }
    }
  }

这样子,我们初版的promise是已经完成了,但是这里缺少了一个关键的点,函数里面如果出现是异步操作就会失败,因为这样子只是在状态改变之后执行函数,如果存在异步,那状态改变之前就不会执行

 const p1 = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve(100)
      }, 100)
    })
    p1.then(value => {
      console.log(value)
    })

如果是这样子的情况,promise的状态还没有改变,就先执行then的函数,就会导致then中的方法没被执行,为了解决异步执行,我们在promise类中新增两个值

// 先实现一个小型的promise
class Promse {
  // 接收一个执行器  两个函数 并且立马执行
  constructor(executor) {
    // 先定义初始状态  初始成功值 初始失败原因
    status = 'pending'
    value = undefined
    reason = undefined
 // 初始化两个数组   用于存放成功的回调函数和失败的回调函数
    onFulfilled = []
    onRejectedFunc = []

    executor(this.resolve, this.reject)
}

新增了两个数组,onFulfilled和onRejectedFunc用于存放成功的回调函数和失败的回调函数,先把需要执行的函数都存放在执行栈中,当状态改变的那一瞬间才去执行函数,这样子就可以解决异步了

  // then方法 接受两个参数
  then (onFulfilled, onRejectedFunc) {
    if (this.status === 'pending') {
      // 如果当前promise是进行中 并且接受到的参数是函数 则把它们放进对应的执行栈中
      // 放进执行栈中 就是为了方便处理异步执行的情况
      if (typeof onFulfilled === 'function') {
        this.onFulfilled.push(onFulfilled)
      }
      if (typeof onRejectedFunc === 'function') {
        this.onRejectedFunc.push(onRejectedFunc)
      }
      // 然后对应状态分别函数
      if (this.status === 'resolved') {
        onFulfilled(this.value)
      }
      if (this.status === 'resolved') {
        onRejectedFunc(this.reason)
      }
    }
  }

现在then方法中添加逻辑,把对应的函数放到对应的执行栈中

resolve = (value) => {
    if (this.status !== 'pending') return
    this.status = 'resolved'
    this.value = value
    this.onFulfilled.forEach(fn => fn())
}
    // 失败返回失败原因
reject = (reason) => {
    if (this.status !== 'pending') return
    this.status = 'rejected'
    this.reason = reason
    this.onRejectedFunc.forEach(fn => fn())
}

在resolve和reject两个函数中去循环调用执行栈的函数,这样子就避免了then先执行,但是状态没被改变的情况

then的链式调用

  • 链式调用的前提是上一个then的返回时一个promise
  • 如果then内部的回调函数执行依然是一个promise,那就把promise的结果resolve出去
  • 任何的promise必须要resolve之后才会到then方法,从而创建下一个promise
  • 当then中返回一个普通值或者一个成功的promise,就走成功的回调
  • 当then中返回一个失败的promise或者抛出异常,就走失败的回调

这里我们添加一个resolvePromise的方法来解决链式调用的问题

  /**
   * 
   * @param {*} promise2 // 链式回调中新的promise
   * @param {*} x // 处理的目标的函数,既上一个promise中的处理函数
   * @param {*} resolve // 新promise的resolve
   * @param {*} reject // 新promise的reject
   */
  resolvePromise (promise2, x, resolve, reject) {
    if (promise2 === x) {
      reject(new TypeError('Promise发生了循环调用, 自己调用自己'))
    }
    // 判断 如果传进来的目标函数是一个对象或者是一个方法 那有可能是promise 就有then
    if (x !== null && (typeof x === 'object') || (typeof x === 'fuction')) {
      // 先获取then方法 为了避免风险 写在try里面
      try {
        setTimeout(() => {
          let then = x.then
          if (typeof then === 'function') {
            // 如果有 就执行他的then里方法
            then.call(x, y => {
              this.resolvePromise(promise2, y, resolve, reject)
            }, r => {
              reject(r)
            })
          } else {
            resolve(x)
          }
        }, 0)
      } catch (e) {
        reject(e)
      }
    } else {
      resolve(x)
    }
  }

当我们接受到一个promise的时候,执行它的then方法,如果链式调用中返回的不是promise,就不用调用下一个的then

然后我们需要修改then方法里面的处理逻辑

  then (onFulfilled, onRejectedFunc) {

    let promise2 = new Promise((resolve, reject) => {
      if (this.status === 'pending') {
        // 如果当前promise是进行中 并且接受到的参数是函数 则把它们放进对应的执行栈中
        // 放进执行栈中 就是为了方便处理异步执行的情况
        if (typeof onFulfilled === 'function') {
          this.onFulfilled.push(() => {
            try {
              let x = onFulfilled(this.value)
              resolvePromise(promise2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          })
        }
        if (typeof onRejectedFunc === 'function') {
          this.onRejectedFunc.push(() => {
            try {
              let x = onRejectedFunc(this.reason)
              this.resolvePromise(promise2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          })
        }
        // 然后对应状态分别函数
        if (this.status === 'resolved') {
          try {
            let x = onFulfilled(this.value)
            resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        }
        if (this.status === 'resolved') {
          try {
            let x = onRejectedFunc(this.reason)
            this.resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        }
      }
    })
  }

在我们在执行then里面的函数时,先创建一个新的promise为了链式的调用,然后再处理对应逻辑时,把新的promise和当前需要处理的函数作为参数传入到resolvePromise方法中,这样子就可以实现链式的调用

本文使用 mdnice 排版