美文网首页
es6:class实现一个promise

es6:class实现一个promise

作者: 安石0 | 来源:发表于2019-05-17 11:21 被阅读0次

    前言:promise的js实现网上有很多,但基本都是基于es5的,es6有着更简介的语法,为什么不尝试一下呢?

    阶段一:

    只支持链式调用不支持其他api

    const pending = 'pending'
    const resolved = 'resolved'
    const rejected = 'rejected'
    class MyPromise {
      constructor (cb) {
        this._status = pending // 初始化状态
        this._data = null // 成功传递的消息
        this._error = null // 失败传递的消息
        this.successSubs = [] // resolve后需要执行的回调队列
        this.failSubs = [] // reject后需要执行的回调队列
        if (typeof cb === 'function') {
          cb(this.resolve.bind(this), this.reject.bind(this)) // 绑定当前实例
        } else if(cb && typeof cb !== 'function') {
          return new TypeError('MyPromise constructor must be a function')
        }
      }
      resolve (_data) {
        // 这里应该有两种情况
        // 1 调用then方法后订阅了
        // 2 直接调用Promise.resolve()方法
        // 情况1:
        if (this._status === pending) {
          this._status = resolved
          this._data = _data
          this.successSubs.forEach(fn => fn())
        }
      }
      reject (_error) {
        if (this._status === pending) {
          this._status = rejected
          this._error = _error
          this.failSubs.forEach(fn => fn())
        }
      }
      resolvePromise (x,resolve, reject) {
        // 如果返回的是MyPromise实例
        if (x instanceof MyPromise) {
          x.then(data => {
            resolve(data)
            },
            error => {
              reject(error)
            })
        } else {
          // 普通值:直接执行then返回的新promise方法的resolve,后一个then属于这个实例的,订阅队列也是属于这个实例的
          resolve(x)
        }
      }
      then (success, fail) {
        // 链式调用 后一个then调用前一个then返回的promise实例
        let p = new MyPromise((resolve, reject) => {
          if (this._status === pending) {
            // promise实例还在pending状态调用了then方法,增加订阅者
            if (success && typeof success === 'function') {
              this.successSubs.push(() => {
                try {
                  // 用户调用then方法,callback函数,两种情况
                  // 1 非promise对象: return 什么就作为参数传递给下个then什么
                  // 2 promise对象:择要等用户的promise的有结果才执行下一个函数
                  let x = success(this._data)
                  // 统一封装到一个函数中处理
                  this.resolvePromise(x, resolve, reject)
                } catch (e) {
                  reject(e)
                }
              })
            }
            if (fail && typeof fail === 'function') {
              this.failSubs.push(() => {
                try {
                  let x  = fail(this._error)
                  this.resolvePromise(x, resolve, reject)
                } catch (e) {
                  reject(e)
                }
              })
            }
          } else if (this._status === resolved) {
            try {
              let x  = success(this._data)
              this.resolvePromise(x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          } else if(this._status === rejected) {
            try {
              let x  = fail(this._error)
              this.resolvePromise(x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          }
        })
        return p
      }
    }
    

    阶段二:

    实现其他api:

    Promise.resolve()和Promise.reject()

    这两个方法都返回一个非pending状态的promise对象,同时Promise类才有的方法,实例没有该方法。和es6中class static方法完美契合。

    ...
     // 直接调用了Promise.resolve方法
      static resolve (value) {
        return new MyPromise(resolve => {
          resolve(value)
        })
      }
      // 直接调用了Promise.reject
      static reject(reason) {
        return new MyPromise((undefined, reject) => {
          reject(reason)
        })
      }
    ...
    

    Promise.all

    Promise.all(iterable): 方法返回一个 Promise 实例,此实例在 iterable 参数内所有的 promise 都“完成(resolved)”或参数中不包含 promise 时回调完成(resolve);如果参数中 promise 有一个失败(rejected),此实例回调失败(reject),失败原因的是第一个失败 promise 的结果。

    整理思路:
    1 判断一个值是否可迭代,可以用Symbol.iterator属性判断,直接用for...of迭代
    2 如果全部正常(没有reject)则可以将该实例resolve。
    3 调用resolve的时机,因为是异步的,不晓得所有的iterable啥时候执行完,所以每次push的时候判断一下是否可以结束,可以结束就resolve。
    4 reject则只需要rejecte当前失败的reason,前面的全部丢弃
    5 static私有方法符合需求

    static all (iterable) {
        if (iterable[Symbol.iterator]) {
          return new MyPromise((resolve, reject) => {
            let resolveArr = []
            let len = iterable.length
            // 检查是不是所有的promise都完成了
            function checkAll () {
              if (resolveArr.length === len) {
                resolve(resolveArr)
              }
            }
            try {
              for (let x of iterable) {
                // 每项可以是Promise值和其他值
                if (x instanceof MyPromise) {
                  x.then(data => {
                    resolveArr.push(data)
                    checkAll()
                  }, reason => {
                    reject(reason)
                  })
                } else {
                  resolveArr.push(x)
                  checkAll()
                }
              }
            } catch (e) {
              reject(e)
            }
          })
        } else {
          // 不是可迭代对象:抛出一个错误
          let str = ({}).toString.call(iterable)
          let reg = /^\[object\s([A-Z][a-z]{2,8})\]$/
          let matchArr = str.match(reg)
          let msg = (matchArr && matchArr[1]) || str
          throw new TypeError( msg + ': is not iterable')
        }
      }
    

    测试mdn的例子:
    结果返回:

    MyPromise
    与mdn的一致
    原生Promise
    再看一个例子:
    image.png
    原生的promise返回结果顺序和传入的一致,我们实现的是谁先resolve就谁先push,显然还需要保证顺序。
    脑海里冒出如下方案:
    1 for循环或者forEach提供了参数index, 和值value方便我们直接赋值,但是Set和Map不支持,而且我们结束的判断resolveArr.length === iterable.length有可能会碰到坑。
    2 提供一个key,用于标记顺序
    目前就想到两个方案,只能选择方案2:
    static all (iterable) {
        if (iterable[Symbol.iterator]) {
          return new MyPromise((resolve, reject) => {
            let resolveArr = []
            let len = iterable.length
            // 检查是不是所有的promise都完成了
            function checkAll () {
              if (resolveArr.length === len) {
                resolve(resolveArr.sort((a, b) => {
                  return a.key - b.key
                }).map(v => v.data))
              }
            }
            try {
              let key = -1
              for (let x of iterable) {
                key++
                // 每项可以是Promise值和其他值
                if (x instanceof MyPromise) {
                  x.then(data => {
                    resolveArr.push({data, key})
                    checkAll()
                  }, reason => {
                    reject(reason)
                  })
                } else {
                  resolveArr.push({data:x, key})
                  checkAll()
                }
              }
            } catch (e) {
              reject(e)
            }
          })
        } else {
          // 不是可迭代对象:抛出一个错误
          let str = ({}).toString.call(iterable)
          let reg = /^\[object\s([A-Z][a-z]{2,8})\]$/
          let matchArr = str.match(reg)
          let msg = (matchArr && matchArr[1]) || str
          throw new TypeError( msg + ': is not iterable')
        }
      }
    

    检测一下:


    image.png

    Promise.race

    Promise.race(iterable) 方法返回一个 promise,一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。

    race方法与all方法类似,但是相对来说更简单:

    static race (iterable) {
        if (iterable[Symbol.iterator]) {
          return new MyPromise((resolve, reject) => {
            try {
              for (let x of iterable) {
                // 每项可以是Promise值和其他值
                if (x instanceof MyPromise) {
                  x.then(data => {
                    resolve(data)
                  }, reason => {
                    reject(reason)
                  })
                } else {
                  resolve(x)
                }
              }
            } catch (e) {
              reject(e)
            }
          })
        } else {
          // 不是可迭代对象:抛出一个错误
          let str = ({}).toString.call(iterable)
          let reg = /^\[object\s([A-Z][a-z]{2,8})\]$/
          let matchArr = str.match(reg)
          let msg = (matchArr && matchArr[1]) || str
          throw new TypeError( msg + ': is not iterable')
        }
      }
    

    测试mdn例子


    image.png

    相关文章

      网友评论

          本文标题:es6:class实现一个promise

          本文链接:https://www.haomeiwen.com/subject/jgynaqtx.html