es6 promise 简单实现

2021-02-05  本文已影响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 必然处于以下几种状态之一:

我们根据状态(监听)来判断 是否要执行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 的功能了。
但上面的代码 不能直接拿来用的,少了 很多 有效性校验。
不过用来面试 应该够了。

上一篇下一篇

猜你喜欢

热点阅读