阅读 97

Promise原理

  想要手写Promise,先透过Promise简单应用来了解其本质,它究竟是如何运行与应用的?
   Promise本质是个类,可以直接new;里面是执行器executor会将函数立即调用,执行器中有两个函数:resolve和reject。

一、Promise声明
  Promise本质是个类,可以直接new,创建Promise类;使用时传递执行器executor,而执行器会存在两个函数参数:resolve与rejec,两者都可调用。所以在类的构造器constructor中参数为执行器,且存在两个函数,会被执行器立即调用。
二、Promise基本状态
  Promise存在三个状态:pending(等待态)、fulfilled(成功态)、rejected(失败态)。
  pending(等待态)为初始态,它可以转化为fulfilled(成功态)和rejected(失败态);成功时不可转为其他状态,且必须有一个不可改变的值(value);失败时也不可转为其他状态,且必须有一个不可改变的原因(reason)
  resolve表示成功,接收参数value,状态改变为fulfilled,不可再次改变。reject表示失败,接收参数reason,状态改变为rejected,不可再次改变。若是executor函数报错,直接执行reject。

class Promise {
  constructor(executor) {
      // 初始化
      // 状态为等待态
      this.state = 'pending'
      // 终值
      this.value = undefined
      // 拒因
      this.reason = undefined

      //成功函数
      let resolve = value => {
          // 状态改变,只能从等待态到成功态
          if (this.state === 'pending') {
              // 成功函数改变状态为成功态
              this.state = 'fulfilled';
              // 终值就是函数参数
              this.value = value
          }
      }
      
      //失败函数
      let reject = (reason) => {
          // 状态改变,只能从等待态到失败态
          if (this.state === 'pending') {
              // 失败函数改变状态为失败态
              this.state = 'rejected';
              // 拒因就是函数参数
              this.reason = reason
          }
      }
      
      // 执行器抛出错误时,会执行reject,且利用catch方法捕获错误
      try {
          // 函数会立即调用
          executor(resolve, reject)
      } catch (err) {
          console.log(err)
      }
  }
}
复制代码

  到这里,仅仅靠着了解Promise的基本应用就可完成一个相当简单自己手写的Promise!是不是可简单,下面就要开始深入挖掘Promise原理咯,紧张起来吧!!!
三、Promise的then方法
  Promise可直接.then操作,那么就代表它是Promise类中的方法,then方法有两个参数:onFulfilled,onRejected;成功有成功的值,失败有失败的原因。 可分为两种情况:
 1)当状态state为fulfilled,则执行onFulfilled,传入this.value。当状态state为rejected,则执行onRejected,传入this.reason。
 2)onFulfilled,onRejected他们是函数,且必须分别在resolve,rejecte函数调用后被调用,而value或reason依次作为他们的第一个参数。

四、Promise异步实现
  现在基本实现简单的同步代码,但是当存在异步代码时,你去调用then方法时state还是pending等待态。所以我们就需要在then调用且仍处于等待态时,将成功函数和失败函数存到各对应的数组,一旦reject或者resolve,就调用它们。
  类似于发布订阅,先将then里面的两个函数储存起来,由于一个promise可以有多个then,所以将成功或者失败的函数分别存在同一个数组内。
五、Promise链式调用
  Promise链式调用,就是不断地.then用来解决回调地狱;要想实现链式调用,需要我们在第一个then里返回一个promise,一般称它为promise2,而这个promise返回的值传递到下一个then中。
  当我们自己在第一个then中return了一个参数(参数未知,需判断)。这个return出来的新的promise就是onFulfilled()或onRejected()的值。
  判断:首先要看第一个then返回的参数是不是promise,通过函数resolvePromise来判断自己返回的参数与规定返回promise的区别,如果我们自己返回的参数是promise,那么将其结果返回到下一个then中;如果是普通值,直接返回到下一个then中。

then(onFulfilled, onRejected) {
        // 首先是定下then的返回值是promise
        // 而在then方法中返回的promise称为promise2
        // promise2返回值不管是啥都传递到下一个thenlet promise2 = new Promise((resolve, reject) => {
            if (this.state === 'fulfilled') {
                // 不确定第一个then返回值是个啥,判断x与promise2
                let x = onFulfilled(this.value)
                // 通过resolvePromise函数
                // 处理自己的返回值x与promise2的关系
                resolvePromise(promise2, x, resolve, reject)
            }
            // 失败
            if (this.state === 'rejected') {
                let x = onRejected(this.reason)
                resolvePromise(promise2, x, resolve, reject)
            }
            // 等待
            if (this.state === 'pending') {
                this.onResolveCallbacks.push(() => {
                    let x = onFulfilled(this.value)
                    resolvePromise(promise2, x, resolve, reject)
                })
                this.onRejectCallbasks.push(() => {
                    let x = onRejected(this.reason)
                    resolvePromise(promise2, x, resolve, reject)
                })
            }
        })
        return promise2
    }
复制代码

六、封装resolvePromise函数
  resolvePromise的作用就是用来判断我们自己返回的第一个then的返回值与promise2的区别。参数由四部分:promise2,x,resolve,reject。
 1)当存在特殊情况x===promise2时,会出现"循环引用"的问题

 let p = new Promise(resolve => {
   resolve(0);
 });
 var p2 = p.then(data => {
  // 循环引用,自己等待自己完成,一辈子完不成
  return p2;
})
复制代码

 2)x值类型排除:不能是null或者undefined;如果是普通值,就直接resolve(x);如果是对象或者是函数(promise),let then = x.then

    if(x!=null&(typeof x==='object'||typeof x==='function')){
        let then = x.then
    }
复制代码

 3)当x是对象或者函数(默认promise),需要做的处理:

  • 声明了then
  • 如果取then报错,则走reject()
  • 如果then是个函数,则用call执行then,第一个参数是this,后面是成功的回调和失败的回调
  • 如果成功的回调还是pormise,就递归继续解析
  • 成功和失败只能调用一个 所以设定一个called来防止多次调用
  function resolvePromise(promise2, x, resolve, reject) {
    // 循环引用错误 
    if (promise2 === x) {
        return reject(new TypeError('Chaining cycle detected for promise'))
    }
    //防止多次调用
    let called;
    // x不是null且x是对象或者函数
    if (x != null & (typeof x === 'object' || typeof x === 'function')) {
        try {
            let then = x.then
            // 如果then是函数,就默认是promise了
            if (typeof then === 'function') {
                // 借助call方法让then执行,第一个参数是this,后面是成功回调和失败回调
                // 成功回调与失败回调都只会执行一次
                then.call(x, data => {
                    if (called) return
                    called = true
                    // 成功回调的结果还是promise,就递归解析
                    // data又是promise,将其解析
                    resolvePromise(promise2, data, resolve, reject)
                }, err => {
                    if (called) return
                    called = true
                    // 失败了就将promise失败的结果向下传递
                    reject(err)
                })
            } else {
                // 新声明的then不是函数,而是对象{then:"tanni"}
                resolve(x)
            }
        } catch (e) {
            if(called)return
            called  = true
            // 这里直接取then时就已是失败,错误捕捉
            reject(e)  
        }
    }else{
        resolve(x)//x是普通值
    }
}
复制代码

七、其它问题
  then方法中onFulfilled,onRejected都是可选参数,如果他们不是函数,必须被忽略。onFulfilled返回一个普通的值,成功时直接等于 value => value;onRejected返回一个普通的值,失败时如果直接等于 value => value,则会跑到下一个then中的onFulfilled中,所以直接扔出一个错误reason => throw err。

  另外onFulfilled或onRejected不能同步被调用,必须异步调用,我们就用setTimeout解决异步问题。如果onFulfilled或onRejected报错,则直接返回reject()

let promise2 = new Promise((resolve, reject) => {
            if (this.state === 'fulfilled') {
                // 必须是异步调用
                setTimeout(() => {
                    // 通过try来捕捉错误
                    try {
                        let x = onFulfilled(this.value)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                })
            }
            // 失败
            if (this.state === 'rejected') {
                setTimeout(() => {
                    try {
                        let x = onRejected(this.reason)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                })
            }
            // 等待
            if (this.state === 'pending') {
                this.onResolveCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onFulfilled(this.value)
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    })
                })
                this.onRejectCallbasks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onRejected(this.reason)
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    })
                })
            }
        })
        return promise2
    }
复制代码

将完整代码为你献上:

   function resolvePromise(promise2, x, resolve, reject) {
    // 循环引用错误 
    if (x === promise2) {
        return reject(new TypeError('Chaining cycle detected for promise'))
    }
    let called;
    // x不是null且x是对象或者函数
    if (x != null && (typeof x === 'object' || typeof x === 'function')) {
        try {
            let then = x.then
            if (typeof then === 'function') {
                then.call(x,data => {
                    if (called) return
                    called = true
                    resolvePromise(promise2, data, resolve, reject)
                }, err => {
                    if (called) return
                    called = true
                    reject(err)
                })
            } else {
                // console.log()
                resolve(x)
            }
        } catch (e) {
            if (called) return
            called = true
            reject(e)
        }
    } else {
        resolve(x)//x是普通值
    }
}

class Promise {
    constructor(executor) {
        this.state = 'pending'
        this.value = undefined
        this.reason = undefined
        this.onResolveCallbacks = []
        this.onRejectCallbasks = []

        //成功函数
        let resolve = value => {
            if (this.state === 'pending') {
                this.state = 'fulfilled';
                this.value = value
                this.onResolveCallbacks.forEach(fn => fn())
            }
        }
        //失败函数
        let reject = (reason) => {
            if (this.state === 'pending') {
                this.state = 'rejected';
                this.reason = reason
                this.onRejectCallbasks.forEach(fn => fn())
            }
        }

        try {
            executor(resolve, reject)
        } catch (err) {
            console.log(err)
        }
    }
    // then方法  
    then(onFulfilled, onRejected) {
        // onFulfilled  onRejected是可选参数,
        // onFulfilled 如果不是函数就可直接忽略,返回value
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
        // onRejected 如果不是函数就可直接忽略,扔出错误
        onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err }

        let promise2 = new Promise((resolve, reject) => {
            if (this.state === 'fulfilled') {
                // 必须是异步调用
                setTimeout(() => {
                    // 通过try来捕捉错误
                    try {
                        let x = onFulfilled(this.value)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                })
            }
            // 失败
            if (this.state === 'rejected') {
                setTimeout(() => {
                    try {
                        let x = onRejected(this.reason)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                })
            }
            // 等待
            if (this.state === 'pending') {
                this.onResolveCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onFulfilled(this.value)
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    })
                })
                this.onRejectCallbasks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onRejected(this.reason)
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    })
                })
            }
        })
        return promise2
    }
}
复制代码

  如果你能跟我理思路梳理到这里,那么恭喜你,就已经成功将promise原理搞个差不多了,当然,如果你能充分的自己手写上述Promise原理代码,那也就真的能熟练掌握Promise原理啦!
本文参考:juejin.im/post/684490…