手写符合 Promises/A+ 规范的 Promises

80 阅读4分钟

1.Promise 的构成

  1. Promise有三个状态PendingFulfilledRejected,表示响应中,成功和失败。
  2. Promise实例有then()resolve()reject()这三个方法可以调用。
  3. Promiseall()race()allSettled()any()这四个静态方法可以使用。
  4. new Promise(fn)的时候我们通常会传入一个函数,并且函数带有两个参数resolvereject,这两个参数也是就是我们的实例方法。

2.基础形态

  1. 我们还需要给有初始化值的方法与初始化initValue(),改变this指向的方法initBind(),下面我们使用Class来写出大概样子。
class Promise {
  // 设置静态属性,三个状态 PENDING、FULFILLED、REJECTED
  static PENDING = 'PENDING' 
  static SUCCESS = 'FULFILLED' 
  static FAIL = 'REJECTED'
  
  // 设置静态属性,终值刚开始为 NULL
  static NULL = null
  
  constructor(executor) { 
    // 初始化值 
    this.initValue()
    // 初始化 this 指向
    this.initBind() 
    try { 
      // 执行传进来的函数,函数的参数就是 resolve 和 reject 两个实例方法 
      executor(this.resolve, this.reject) 
    } catch (e) { 
      // 捕捉到错误直接执行 
      reject this.reject(e)
    } 
  }
  
  // 初始化值的方法
  initValue() { 
    this.PromiseState = Promise.PENDING // 状态 
    this.PromiseResult = Promise.NULL // 终值结果 
    this.onFulfillCallbacks = [] // 保存成功的回调函数 
    this.onRejectCallbacks = [] // 保存失败的回调函数 
  }
  
  // 初始化this指向的方法
  initBind() { 
    this.resolve = this.resolve.bind(this) 
    this.reject = this.reject.bind(this) 
  }
  
  resolve(value) { 
    // state 一经改变就是不可变的 
    if (this.PromiseState !== Promise.PENDING) return 
    // 如果执行resolve,状态变为 FULFILLED 
    this.PromiseState = Promise.SUCCESS 
    // 终值为传进来的值 
    this.PromiseResult = value 
    // 执行保存的成功回调函数 
    while (this.onFulfillCallbacks.length) { 
      // 循环取数组里第一个方法执行 
      this.onFulfillCallbacks.shift()(this.PromiseResult) 
    } 
  }
  
  reject(reason) { 
    // state 一经改变就是不可变的 
    if (this.PromiseState !== Promise.PENDING) return 
    // 如果执行reject,状态变为 REJECTED 
    this.PromiseState = Promise.FAIL 
    // 终值为传进来的reason 
    this.PromiseResult = reason 
    // 执行保存的失败回调函数 
    while (this.onRejectCallbacks.length) { 
      // 循环取数组里第一个方法执行 
      this.onRejectCallbacks.shift()(this.PromiseResult) 
    } 
  }
}

then(onFulfilled, onRejected) {} 
// 其他方法 
static all(promises) {} 
static race(promises) {} 
static allSettled(promises) {} 
static any(promises) {}

3.then 方法

  1. then 方法可以传入两个参数,第一个参数是状态为FULFILLED后的会执行回调函数,第二个参数是状态为REJECTED时会执行的回调函数。
// 接收两个回调 onFulfilled, onRejected
then(onFulfilled, onRejected) { 
  // 参数校验,确保参数为函数,否则就把参数返回 
  onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
  onRejected = typeof onRejected === 'function' ? onRejected : reason => throw reason
  const thenPromise = new Promise((resolve, reject) => { 
    const resolvePromise = cb => { 
      queueMicrotask(() => { 
        try { 
          const x = cb(this.PromiseResult) 
          if (thenPromise === x) { 
            // 不能返回自身 throw new Error('不能返回自己') 
          } else if (x instanceof Promise) { 
            // 如果返回值是Promise,返回值为成功,新promise就是成功 
            // 返回值为失败,新promise就是失败 
            // 谁知道返回的promise是失败成功?只有then知道 
            x.then(resolve, reject) 
          } else { 
            // 非Promise就直接成功 
            resolve(x) 
          } 
        } catch (e) { 
          // 处理报错 reject(e) 
        } 
      }) 
    } 
    if (this.PromiseState === Promise.SUCCESS) { 
      // 如果当前为成功状态,执行第一个回调函数 
      resolvePromise(onFulfilled) 
    } else if (this.PromiseState === Promise.FAIL) {
      // 如果当前为失败状态,执行第二个回调函数 
      resolvePromise(onRejected) 
    } else if (this.PromiseState === Promise.PENDING) { 
      // 如果状态为待定状态,暂时保存两个回调函数到对应的队列里
      this.onFulfillCallbacks.push(resolvePromise.bind(this, onFulfilled))
      this.onRejectCallbacks.push(resolvePromise.bind(this, onRejected)) 
    } 
  }) 
  return thenPromise

4.all 方法

  1. all方法有一个参数,是一个数组,数组里的元素都为promise,当所有的promise都为成功时才能返回一个数组,元素是所有成功的结果,只要有一个promise失败就会将这个失败的结果返回。
all(promises) { 
  // 搜集结果的数组
  const result = [] 
  let count = 0 
  // 返回的结果也是一个promise
  return new Promise((resolve, reject) => { 
    // 一个方法,当参数里的promise成功时就把结果放到 result 中
    const addData = (index, value) => { 
      result[index] = value count++ 
      // 如果能执行到最后一个promise了说明所有promise都执行成功了,返回result
      if (count === promises.length) { 
        resolve(result) 
      } 
    } 
    // 遍历all方法传入的数组
    promises.forEach((promise, index) => { 
      if (promise instanceof Promise) { 
        promise.then(res => {
          // 当执行成功时把结果加入result
          addData(index, res) 
        }, err => {
          // 当失败时就调用 reject,并把错误结果返回
          reject(err)
        }) 
      } else { 
        // 当传入的数组元素部位 Promise 时,默认成功
        addData(index, promise) 
      } 
    }) 
  }) 
}

5.race 方法

  1. 参数与all方法一样,传入多个promise组成的数组,返回的结果第一个有结果的promise,不管是成功还是失败。
race(promises) { 
  return new Promise((resolve, reject) => { 
    // 执行所有 promise
    promises.forEach(promise => { 
      if (promise instanceof Promise) { 
        // 当有一个 promise 有结果就返回
        promise.then(res => { 
          resolve(res) 
        }, err => { 
          reject(err) 
        }) 
      } else { 
        // 传入的数组元素不为 promise默认成功,返回本身
        resolve(promise) 
      } 
    }) 
  }) 
}

6.allSettled 方法

  1. 参数为多个promise组成的数组,返回的所有的promise状态和结果,不管是成功还是失败。
allSettled(promises) { 
  return new Promise((resolve, reject) => { 
    // 收集结果的数组
    const res = [] 
    let count = 0 
    // 将结果加入数组的方法
    const addData = (status, value, i) => { 
      res[i] = { status, value } 
      count++ 
      if (count === promises.length) { 
        resolve(res) 
      } 
    } 
    // 便利数组
    promises.forEach((promise, i) => { 
      if (promise instanceof Promise) { 
        promise.then(res => { 
          // 添加状态与结果
          addData('fulfilled', res, i) 
        }, err => { 
          // 添加状态与结果
          addData('rejected', err, i) 
        }) 
      } else { 
        当数组元素部位 Promise 时,默认成功,结果为元素本身
        addData('fulfilled', promise, i) 
      } 
    }) 
  })
}

7.any 方法

  1. 参数为多个promise组成的数组,只要有一个promise状态为成功,那么就算成功,返回第一个成功promise的结果,如果所有的promise都失败,那么就会报错。
any(promises) { 
  return new Promise((resolve, reject) => { 
    let count = 0 
    promises.forEach((promise) => { 
      promise.then(val => { 
        resolve(val) 
      }, err => { 
        count++ 
        if (count === promises.length) { 
          reject(new AggregateError('All promises were rejected')) 
        } 
      }) 
    }) 
  }) 
}