手写一个Promise

in JavaScript with 0 comment

Promise主要为了解决回调函数在js中使用的缺点而诞生的,比如现在实现一个串行任务,这个串行任务包括三个任务,如果用回调来实现就会造成地域回调。

手写Promise来模拟实现原生Promise的效果其实不难,重点在于弄清楚Promise的三个状态,以及三个状态对应的操作, 主要要搞清楚then函数的逻辑, promise只有状态改变之后才会执行then方法,并且then方法返回一个新的promise, 并且then中的方法是异步执行的,当Promise异步resolve时,要把then中的两个回调函数添加到callback数组中,在promise resolve的时候再去数组中取出调用即可,还有一点需要注意,即resolveFn这个回调函数也需要异步执行

本示例中的异步均使用 setTimeout来模拟,实际实现中应该不是用的setTimeout,这样会造成性能瓶颈。

下边使用ES6面向对象的方法来实现一版, 具体函数都有注释,这里为了便于理解,有些耦合性高的地方没有处理,实际上可以把try/catch 提取一下解耦,这里的代码是参考B站上一个教程写的,那个老师(后盾人)讲的深入浅出,比较适合新手理解。如果有遇到不理解的,可以debug一下,基本上就没有什么问题了

class MyPromise {
        static PENDING = 'pending'
        static FULFILLED = 'fulfilled'
        static REJECTED = 'rejected'
        //构造函数
        constructor(executor) {
           this.status = MyPromise.PENDING
           this.data = null
           //把回调函数以对象形式放到数组中,只需要定义一个数组即可
           this.callback = []
           //this.resolve = this.resolve.bind(this)
           //executor函数执行可能出错
           try{
             //this绑定
             executor(this.resolve.bind(this), this.reject.bind(this))
           }catch(e) {
             //如果executor函数出错,则promise的状态为rejected
             this.reject(e)
           }
        }
        //js面向对象方法之间不能加逗号分隔,同时方法不需要添加 function 关键字
        resolve(value) {
          if(this.status === MyPromise.PENDING) {
            this.status = MyPromise.FULFILLED
            this.data = value
            //resolve的时候处理回调, 回调函数也要异步执行
            for(let fn of this.callback) {
              setTimeout(() => {
                fn.resolvedFncb(value)
              })
            }
          }
        }
        //类方法reject
        reject(reason) {
          if(this.status === MyPromise.PENDING) {
            this.status = MyPromise.REJECTED
            this.data = reason
            //reject的时候处理回调, 回调函数也要异步执行
            for(let fn of this.callback) {
              setTimeout(() => {
                fn.rejectedFncb(reason)
              })
            }
          }
          
        }
        //then定义为返回一个新的MyPromise
        then(resolvedFn, rejectedFn) {
         //console.log(resolvedFn)
         resolvedFn = typeof resolvedFn === 'function' ? resolvedFn : function(v){return v}
         rejectedFn = typeof rejectedFn === 'function' ? rejectedFn : function(v){throw v}
         
         let p = new MyPromise((resolve, reject) => {
          //console.log('我在这')
            //此处this 为第一次new 的 Promise
            //console.log(this)
            if(this.status === MyPromise.FULFILLED) {
              //调试时为何一下从setTimeout跳到了 this.status === MyPromise.PENDING
              //猜测是运行了另一个then,打断点测试看看,是的,因为resolve那里是异步的
              setTimeout(() => {
                  try{
                    let x = resolvedFn(this.data)
                    if(p === x) {
                      throw new TypeError('Chaining cycle detected for promise')
                    }
                    if(x instanceof MyPromise) {
                      //这样写值如何传递过去?
                      //其实这样写,resolve/reject 函数肯定会被调用, 这时 resolvedFn ==> resolve
                      //然后绕一圈会再到 let x = resolvedFn(this.data) ==> let x = resolve(this.data) <--->跟 resolve(res) 效果是一样的
                      //比较巧妙
                      x.then(resolve, reject)
                      //这样写就知道了
                      // x.then(res => {
                      //   resolve(res)
                      // }, err => {
                      //   reject(err)
                      // })
                      
                      //console.log(x)
                    }else{
                      resolve(x)
                    }
                  }catch(e) {
                    rejectedFn(e)
                  }
              })
            }
            if(this.status === MyPromise.REJECTED) {
              setTimeout(() => {
                try{
                  let x = rejectedFn(this.data)
                  if(p === x) {
                      throw new TypeError('Chaining cycle detected for promise')
                  }
                  if(x instanceof MyPromise) {
                    x.then(resolve, reject)
                  }else{
                    resolve(x)
                  }
                }catch(e) {
                  rejectedFn(e)
                }
              }) 
            }
            if(this.status === MyPromise.PENDING) {
              //console.log('我在这')
              this.callback.push({
                resolvedFncb: (value) => {
                  try{
                    let x = resolvedFn(value)
                    if(p === x) {
                      throw new TypeError('Chaining cycle detected for promise')
                    }
                    if(x instanceof MyPromise) {
                      x.then(resolve, reject)
                    }else{
                      resolve(x)
                    }
                  }catch(e) {
                    rejectedFn(e)
                  }
                },
                rejectedFncb: (reason) => {
                  try{
                    let x = rejectedFn(reason)
                    if(p === x) {
                      throw new TypeError('Chaining cycle detected for promise')
                    }
                    if(x instanceof MyPromise) {
                      x.then(resolve, reject)
                    }else{
                      resolve(x)
                    }
                  }catch(e) {
                    rejectedFn(e)
                  }
                }
              })
              //this.callback.push(resolvedFn)
            }
         })
         return p
        }
        //静态resolve方法, MyPromise.resolve()返回一个MyPromise
        static resolve(value) {
          return new MyPromise((resolve, reject) => {
            if(value instanceof MyPromise) {
              //写法一,容易理解
              value.then(res => {
                resolve(res)
              }, err => {
                reject(err)
              })
            }else{
              resolve(value)
            } 
          })
        }
        static reject(reason) {
          return new MyPromise((resolve, reject) => {
            if(reason instanceof MyPromise) {
              //写法二,不太容易理解
              reason.then(resolve, reject)
            }else{
              reject(reason)
            }
          })
        }
        //这里的实现不能按照添加顺序返回结果
        static all(promises) {
          let result = []
          return new MyPromise((resolve, reject) => {
            //promises中有一个reject,则reject
            //promises所有resolve, 则返回[res1, res2, res3]
            // for(let p of promises) {
            //   if(p.status === MyPromise.REJECTED){
            //     reject()
            //   }else{
            //     p.then(res => {
            //       result.push(res)
            //       resolve()
            //     })
            //   }
            // }
            for(let p of promises) {
              p.then(res => {
                result.push(res)
                if(result.length === promises.length) {
                  resolve(result)
                }
              }, err => {
                reject(err)
                //throw new Error('Uncaught (in promise) ' + err)
              })
            }
          })
          //return 
        }
        static race(promises) {
          return new MyPromise((resolve, reject) => {
            //for循环里的逻辑是同步的
            for(let p of promises) {
              p.then(res => {
                resolve(res)
              }, err => {
                reject(err)
              })
            }
          })
        }
      }

参考:

知乎: 史上最易读懂的 Promise/A+ 完全实现

评论已关闭.