promise 的原理+简单实现

221 阅读4分钟

promise 规范+简单实现

PromiseA+规范

术语

  1. Promise是一个有than方法的对象或者函数,行为遵循A+规范
  2. thenable也是一个有then方法的对象或函数
  3. value是promise转为fulfiled时的值,也是resolve的参数
  4. reason是promise转为rejected时的拒因
  5. exception 是一个使用throw抛出了的异常值

规范如下:

states

promise有三种状态

  1. pendding 1.1 初始的状态。可以改变 1.2 一个promise在resolve和reject之前都 处于这个状态 1.3 可以 resolve-> fulfilled状态 1.4 可以 reject-> rejected状态
  2. fulfilled 2.1 最终值 不可变 2.2 通过reaolve 变为fulfilled 2.3 必须有一个value值
  3. rejected 3.1 最终值, 不可变 3.2 通过reject 变成rejected 3.3 必须有一个reason值

then

promise必须提供一个then方法用来访问最终的结果,value或者reason

promise.then(onFulfilled,onRejected)
  1. 参数要求 1.1 onFullfilled 必须是一个函数 否则被忽略
realFulfilled = this.isFunction(onFulfilled) ? onFulfilled : value=>value

1.2 onRejected 必须是一个函数 否则被忽略

realRejected = this.isFunction(onRejected) ? onRejected: reason=>{throw new Error('xxx')}
  1. onFulfilled 特性 2.1 在状态变为fulfilled时调用 参数是value 2.2 状态改变之前 不应该调用 2.3 只能调用一次

  2. onRejected 特性 3.1 在状态变为rejected时调用 参数是reason 3.2 状态改变之前 不应该调用 3.3 只能调用一次

  3. onFulfilled和onRejected应该是微任务

  4. then方法可以被调用多次 5.1 promise状态变成 fulfilled 后,所有的 onFulfilled 回调都需要按照then的顺序执行, 也就是按照注册顺序执行(所以在实现的时候需要一个数组来存放多个onFulfilled的回调) 5.2 promise状态变成 rejected 后,所有的 onRejected 回调都需要按照then的顺序执行, 也就是按照注册顺序执行(所以在实现的时候需要一个数组来存放多个onRejected的回调)

  5. 返回值 then方法返回一个promise

  promise2 = promise1.then(onFulfilled, onRejected);
  ```

  6.1 onFulfilled或onRejected的结果为x, 调用 resolvePromise
  6.2 如果 onFulfilled 或者 onRejected 执行时抛出异常e, promise2需要被reject
  6.3 如果 onFulfilled 不是一个函数, promise2 以promise1的value 触发fulfilled 值的透传
  ```js
  new Promise((resolve,reject)=>{
    resolve(1)
  })
  .then(3)
  .then(3)
  .then(res=>{
    console.log(res)
  })
  // 1
  ```
  6.4 如果 onRejected 不是一个函数, promise2 以promise1的reason 触发rejected 值的透传

7. resolvePromise(promise2,x,resolve,reject)
7.1 r如果promise2 和x相等 则return Error 防止死循环
    ```js
    if(x===promise2) {
      reject(new TypeError('xxxx'))
    
    }
    ```
7.2 如果 x 是一个 promsie
    如果x是pending态,那么promise必须要在pending,直到 x 变成 fulfilled or rejected.
    如果 x 被 fulfilled, fulfill promise with the same value.
    如果 x 被 rejected, reject promise with the same reason.
    ```js
    x.then(
      y=>{
        this.resolvePromise(promise2,y,resolve,reject)
      },
      reject
    )
    ```
7.3 如果 x 是一个 object 或者 是一个 function
      如果 x.then 这步出错,那么 reject promise with e as the reason

      ```js
      let then
      try {
        then = x.then
      } catch(e) {
        reject(e)
      }
      ```
      如果 then 是一个函数,then.call(x, resolvePromiseFn, rejectPromise)
          resolvePromiseFn 的 入参是 y, 执行 resolvePromise(promise2, y, resolve, reject);
          rejectPromise 的 入参是 r, reject promise with r.
          如果 resolvePromise 和 rejectPromise 都调用了,那么第一个调用优先,后面的调用忽略。
          如果调用then抛出异常e 
              如果 resolvePromise 或 rejectPromise 已经被调用,那么忽略
              则,reject promise with e as the reason

        ```js
        if (this.isFunction(then)) {
          let called = false
          try {
            then.call(
              x,
              y=>{
                if (called) return
                called = true
                this.resolvePromise(promise2,y,resolve,reject)
              },
              r=>{
                if (called) return
                called = true
                reject(r)
              }
            )
          } catch(e) {
            reject(e)
          }
        } else {
          resolve(x)
        }
        ```
      如果 then 不是一个function. fulfill promise with x.

### 完整代码
```js

const PENDDING= 'pendding'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class LPromise {
FULFILLED_CB_LIST= []
REJECTED_CB_LIST = []
constructor (fn) {
  this.value = undefined
  this.reason = undefined
  this.status = PENDDING
  try {
    fn(this.resolve.bind(this), this.reject.bind(this))
  } catch (error) {
    this.reject(error)
  }
}
resolve(value) {
  if (this.status === PENDDING) {
    this.status = FULFILLED
    this.value = value
    if(this.FULFILLED_CB_LIST.length){
      this.FULFILLED_CB_LIST.forEach(cb=>cb(this.value))
    }
  }
}
reject(reason){
  if (this.status === PENDDING){
    this.status = REJECTED
    this.reason = reason
    if(this.REJECTED_CB_LIST.length){
      this.REJECTED_CB_LIST.forEach(cb=>cb(this.reason))
    }
  }
}
then(onFulfilled, onRejected){
  const realFulfilled = this.isFunction(onFulfilled) ? onFulfilled : value => value
  const realRejected = this.isFunction(onRejected) ? onRejected : reason=>{throw Error(reason)}
  const promise2 = new LPromise((resolve,reject)=>{
    const fulflledMircotask = ()=>{
      queueMicrotask (()=>{
        try {
          const x = realFulfilled(this.value)
          this.resolvePromise(promise2,x,resolve,reject)
        } catch (error) {
          reject(error)
        }
      })
    }
    const rejectedMircotask = ()=>{
      queueMicrotask(()=>{
        try {
          const x = realRejected(this.reason)
          this.resolvePromise(promise2,x,resolve,reject)
        } catch (error) {
          reject(error)
        }
      })
    }
    switch (this.status) {
      case FULFILLED: {
        fulflledMircotask()
        break
      }
      case REJECTED: {
        rejectedMircotask()
        break
      }
      case PENDDING:{
        this.FULFILLED_CB_LIST.push(fulflledMircotask)
        this.REJECTED_CB_LIST.push(rejectedMircotask)
        break
      }
    }
  })
  return promise2
}
resolvePromise(promise2,x,resolve,reject){
  if (promise2 === x) {
    return reject(new TypeError('can not return the same value'))
  }
  if (x instanceof LPromise) {
    queueMicrotask(()=>{
      x.then(
        y=>{
          this.resolvePromise(promise2,y,resolve,reject)
        },
        r=>{
          reject(r)
        }
      )
    })
  }
  if (x === null) {
    return resolve(x)
  }
  if (typeof x === 'object' || this.isFunction(x)) {
    let then
    try {
      then = x.then
      if (this.isFunction(then)) {
        let used = false
        then.call(
          x,
          y=>{
            if(used) return
            used = true
            this.resolvePromise(promise2,y,resolve,reject)
          },
          r=>{
            if(used) return
            used = true
            reject(r)
          }
          )
      } else {
        resolve(x)
      }
    } catch (error) {
      reject(error)
    }
  } else {
    resolve(x)
  }
}
isFunction(fn){
  return typeof fn === 'function'
}
}

var p1 = new LPromise((resolve,reject)=>{
resolve('111')
}).then((res)=>{
console.log('res',res)
return 'resolve1111'
},r=>{
console.log('r',r)
return 'reject1111'
}).then(res=>{
console.log('d第二个then的res',res)
})