美文网首页
es6 promise 简单实现

es6 promise 简单实现

作者: 草祭木初 | 来源:发表于2021-02-05 17:58 被阅读0次

    Promise定义

    来看一个最简单的Promise的结构

    new Promise((resolve, reject) => {
    
    }).then((res)=>{
    
    }).catch((e)=>{
    
    }).finally(()=>{
        
    })
    

    根据Promise定义 我们很容易写出它的接口定义

      function Promise(resolver) {
    
      }
      Promise.prototype.then = function (onFulfilled, onRejected) {
    
      }
      Promise.prototype.catch = function (onRejected) {
    
      }  
      Promise.prototype.finally = function (onFinally) {
        
      }
    

    然后我们有几个问题要解决
    1,链式调用
    2,顺序调用

    1,链式调用 很简单,用过jquery的都知道。只要每个函数都返回当前对象就可以

    我们暂且可以这样写

     Promise.prototype.then = function (onFulfilled, onRejected) {
        return this
     }
    

    2,顺序调用

    其实就是,所有函数里回调函数的 调用顺序。
    从构造函数起始
    Promise resolve -> then -> finally
    Promise reject -> catch -> finally

    所以,事实上把 then catch finally 看作是注册函数,
    向Promise上 注册了,thenResolveCallBack,thenRejectCallBack,catchCallBack,finallyCallBack

    然后代码可以变成下面的样子(先不要考虑 参数校验)

      function Promise(resolver) {
        this.thenResolveCallBack = undefined;
        this.thenRejectCallBack = undefined;
        this.catchCallBack = undefined;
        this.finallyCallBack = undefined;
        const self = this;
        function resolve(value) {
          self.thenResolveCallBack && self.thenResolveCallBack(value)
          self.finallyCallBack && self .finallyCallBack(value)
        }
    
        function reject(value) {
          self.catchCallBack && self.catchCallBack(value)
          self.finallyCallBack && self.finallyCallBack(value)
        }
    
        resolver && resolver(resolve, reject)
    
      }
      Promise.prototype.then = function (onFulfilled, onRejected) {
        this.thenResolveCallBack = onFulfilled
        this.thenRejectCallBack = onRejected
        return this
      }
      Promise.prototype.catch = function (onRejected) {
        this.catchCallBack = onRejected
        return this
      }
      Promise.prototype.finally = function (onFinally) {
        this.finallyCallBack = onFinally
        return this
      }
    

    调用

      new Promise((resolve, reject) => {
        console.log('this is promise constructor')
        resolve('resolve is call')
      }).then((value) => {
        console.log('this is then. value:', value)
      }).catch((value) => {
        console.log('this is catch. value:', value)
      }).finally((value) => {
        console.log('this is finally. value:', value)
      })
    

    你会发现 只有构造函数里的log打印出来了
    问题出现在,构造函数里的这句

    resolver && resolver(resolve, reject)
    

    因为resolve是同步调用的
    所以then 与 finally 还没有注册到Promise上,就完成调用了。
    即使resolve是异步调用的,我们也不能保证resovle是在then catch finally 注册完成后才调用

    接下来我们就来解决这个问题
    先回忆下Promise定义

    一个 Promise 必然处于以下几种状态之一:

    • 待定(pending): 初始状态,既没有被兑现,也没有被拒绝。
    • 已兑现(fulfilled): 意味着操作成功完成。
    • 已拒绝(rejected): 意味着操作失败。

    我们根据状态(监听)来判断 是否要执行then catch finally
    于是代码变成下面样子

      const PENDING = 0;
      const FULFILLED = 1;
      const REJECTED = 2;
    
      function Promise(resolver) {
        this.thenResolveCallBack = undefined;
        this.thenRejectCallBack = undefined;
        this.catchCallBack = undefined;
        this.finallyCallBack = undefined;
    
        this._status = PENDING;
    
        function resolve(value) {
          this._status = FULFILLED;
          this.thenResolveCallBack && this.thenResolveCallBack(value)
          this.finallyCallBack && this.finallyCallBack(value)
        }
    
        function reject(value) {
          this._status = REJECTED;
          this.catchCallBack && this.catchCallBack(value)
          this.finallyCallBack && this.finallyCallBack(value)
        }
    
        resolver && resolver(resolve, reject)
    
      }
      Promise.prototype.then = function (onFulfilled, onRejected) {
        this.thenResolveCallBack = onFulfilled
        this.thenRejectCallBack = onRejected
        if (this._status === FULFILLED) {
          this.thenResolveCallBack(value)
        }
        return this
      }
      Promise.prototype.catch = function (onRejected) {
        this.catchCallBack = onRejected
        if (this._status === REJECTED) {
          this.catchCallBack(value)
        }
        return this
      }
      Promise.prototype.finally = function (onFinally) {
        this.finallyCallBack = onFinally
        if (this._status) {
          this.finallyCallBack(value)
        }
        return this
      }
    

    至此最简单的Promise就实现了

    3,但,还没有结束

    来看下面这种情况, 有两个then, 链式调用是可以这样的

      new Promise((resolve, reject) => {
        console.log('this is promise constractor')
        setTimeout(()=>{resolve('resolve is call')}, 1000)      
      }).then((value)=>{
        console.log('this is then1. value:', value)
      }).then((value)=>{
        console.log('this is then2. value:', value)
      }).catch((value)=>{
        console.log('this is catch. value:', value)
      }).finally((value)=> {
        console.log('this is finally. value:', value)
      })
    

    我们之前的代码是不支持这种情况的。
    所以我们要改下代码,把写死的callback换成,动态。而且要有顺序。
    所以【数组】很适合做这件事。
    例如:[then, then, catch, finally]
    但,这样我们不知道是否要执行位置 i 上的回调
    两种解决方案
    1,then: {
    type: 'then',
    onFulfilled: onFulfilled
    }
    2, then: {
    onFulfilled: onFulfilled,
    onRejected: onRejected
    }
    catch: {
    onFulfilled: undefined
    onRejected: onRejected
    }
    其实还有其他解决方案,我这里采用第二种
    于是代码改造成

      const PENDING = 0;
      const FULFILLED = 1;
      const REJECTED = 2;
    
      function Promise(resolver) {
        // 回调(监听)队列
        this._subscribers = [];
    
        this._status = PENDING;
        this._value = undefined;
    
        const self = this
        function resolve(value) {
          self._status = FULFILLED;
          self._value = value;
          subscribe()
        }
    
        function reject(value) {
          self._status = REJECTED;
          self._value = value;
          subscribe()
        }
    
        function subscribe() {
          self._subscribers.forEach(item => {
            if (self._status === FULFILLED && item.onFulfilled) {
              item.onFulfilled(self._value)
            } 
            if (self._status === REJECTED && item.onRejected) {
              item.onRejected(self._value)
            }
          })
        }
    
        resolver && resolver(resolve, reject)
    
      }
      Promise.prototype.then = function (onFulfilled, onRejected) {
        this._subscribers.push({
          onFulfilled: onFulfilled,
          onRejected: onRejected
        })
        if (this._status === FULFILLED) {
          onFulfilled(this._value)
        }
        return this
      }
      Promise.prototype.catch = function (onRejected) {
        this._subscribers.push({
          onRejected: onRejected
        })
        if (this._status === REJECTED) {
          onRejected(this._value)
        }
        return this
      }
      Promise.prototype.finally = function (onFinally) {
        this._subscribers.push({
          onFulfilled: onFinally
        })
        if (this._status) {
          onFinally(this._value)
        }
        return this
      }
    

    4,根据Promise定义
    then,catch里是可以返回一个Promise的

    例如这样:

      new Promise((resolve, reject) => {
        console.log('this is promise1 constractor')
        setTimeout(() => { resolve('resolve is call') }, 1000)
      }).then((value) => {
        console.log('this is then1. value:', value)
        return new Promise((resolve, reject) => {
          console.log('this is promise2 constractor')
        })
      }).then((value) => {
        console.log('this is then2. value:', value)
      }).catch((value) => {
        console.log('this is catch. value:', value)
      }).finally((value) => {
        console.log('this is finally. value:', value)
      })
    

    所以执行路径变成
    promise1 -> then1 -> promise2 -> then2 -> finally
    promise1 -> then1 -> promise2 -> catch -> finally
    promise1 -> catch -> finally

    我们的主要目的是 拿到promise2里的 value继续往下传
    因为 后面的 catch finally 都是注册在 promise1 上的
    所以我们要做两件事
    1,执行到promise2的时候, promise1的subscribe 要停下,等待promise2执行完
    2,promise2执行完后,promise1的subscribe要继续执行下去

    解决方案
    1,执行到promise2的时候, promise1的subscribe 要停下,等待promise2执行完
    这个很简单:
    可以记住执行到数组的哪个位置(比较麻烦)
    也可以给监听者对象添加一个状态(我们采用这种)

    2,promise2执行完后,promise1的subscribe要继续执行下去
    这个其实也很简单,只要你明白promise2也有个监听者列表,
    所以只要我们给promise2的监听者列表添加一个监听者,我们就可以做我们想做的事了

    上代码

      const PENDING = 0;
      const FULFILLED = 1;
      const REJECTED = 2;
    
      function Promise(resolver) {
        // 回调(监听)队列
        this._subscribers = [];
    
        this._status = PENDING;
        this._value = undefined;
    
        const self = this
    
        this.subscribe = function () {
          const length = self._subscribers.length
          for (let i = 0; i < length; i++) {
            const item = self._subscribers[i];
            if (item.status === PENDING) {
              let nextPromise;
              if (self._status === FULFILLED && item.onFulfilled) {
                nextPromise = item.onFulfilled(self._value);
                item.status = FULFILLED;
              }
              if (self._status === REJECTED && item.onRejected) {
                nextPromise = item.onRejected(self._value);
                item.status = REJECTED;
              }
              if (nextPromise) {
                if (nextPromise._status) {
                  self._status = nextPromise._status
                  self._value = nextPromise._value
                } else {
                  nextPromise._subscribers.push({
                    status: PENDING,
                    onFulfilled: (value) => { resolve(value) },
                    onRejected: (value) => { reject(value) }
                  })
                }
    
                break;
              }
            }
          }
    
        }
    
        function resolve(value) {
          self._status = FULFILLED;
          self._value = value;
          self.subscribe()
        }
    
        function reject(value) {
          self._status = REJECTED;
          self._value = value;
          self.subscribe()
        }
    
        resolver && resolver(resolve, reject)
    
      }
      Promise.prototype.then = function (onFulfilled, onRejected) {
        this._subscribers.push({
          onFulfilled: onFulfilled,
          onRejected: onRejected,
          status: PENDING
        })
        this.subscribe()
        return this
      }
      Promise.prototype.catch = function (onRejected) {
        this._subscribers.push({
          onRejected: onRejected,
          status: PENDING
        })
        this.subscribe()
        return this
      }
      Promise.prototype.finally = function (onFinally) {
        this._subscribers.push({
          onFulfilled: onFinally,
          status: PENDING
        })
        this.subscribe()
        return this
      }
    

    好了 总是基本完成了 Promise 的功能了。
    但上面的代码 不能直接拿来用的,少了 很多 有效性校验。
    不过用来面试 应该够了。

    相关文章

      网友评论

          本文标题:es6 promise 简单实现

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