Promise实现

219 阅读9分钟

Promise

promise/A+规范

主要功能

实现过程

同步逻辑

Promise 是一个带有 then 方法的 object 或一个 function

class Promise {
  then(onFulfilled, onRejected) {
  }
}

Promise 接受一个参数 executor 执行器 默认会立即执行

class Promise {
  /**
   * @param {function} executor - 执行器
   */
  constructor(executor) {
    executor()
  }
  then(onFulfilled, onRejected) {
    console.log(onFulfilled, onRejected)
  }
}

一个 Promise 必须处于以下三种状态之一: pending, fulfilled, rejected, 默认处于等待态

const ENUM = {
  PENDING: 'PENDING',// 等待
  FULFILLED: 'FULFILLED',// 成功
  REJECTED: 'REJECTED'// 失败
}

class Promise {
  /**
   * @param {function} executor - 执行器
   */
  constructor(executor) {
    this.status = ENUM.PENDING// 默认等待态
    executor()
  }
  then(onFulfilled, onRejected) {
    console.log(onFulfilled, onRejected)
  }
}

executor 接受了两个参数 成功的回调和失败的回调

const ENUM = {
  PENDING: 'PENDING',// 等待
  FULFILLED: 'FULFILLED',// 成功
  REJECTED: 'REJECTED'// 失败
}

class Promise {
  /**
   * @param {function} executor - 执行器
   */
  constructor(executor) {
    this.status = ENUM.PENDING// 默认等待态

    const resolve = value => {// 成功回调

    }
    const reject = reason => {// 失败回调

    }

    executor(resolve, reject)
  }
  then(onFulfilled, onRejected) {
    console.log(onFulfilled, onRejected)
  }
}

当 Promise 状态为 pending 时 可以转变为 fulfilled 或 rejected 状态

当 Promise 状态为 fulfilled 时 不能转变为其他状态, 必须接收一个不能被改变的 value 可以是任意合法的JS值

当 Promise 状态为 rejected 时 不能转变为其他状态, 必须接收一个不能被改变的 reason 指出失败原因

const ENUM = {
  PENDING: 'PENDING',// 等待
  FULFILLED: 'FULFILLED',// 成功
  REJECTED: 'REJECTED'// 失败
}

class Promise {
  /**
   * @param {function} executor - 执行器
   */
  constructor(executor) {
    this.status = ENUM.PENDING// 默认等待态

    const resolve = value => {// 成功回调
      if (this.status === ENUM.PENDING) {// 处于 pending 状态才能改变状态
        this.status = ENUM.FULFILLED
        this.value = value
      }
    }
    const reject = reason => {// 失败回调
      if (this.status === ENUM.PENDING) {
        this.status = ENUM.REJECTED
        this.reason = reason
      }
    }

    executor(resolve, reject)
  }
  then(onFulfilled, onRejected) {
    console.log(onFulfilled, onRejected)
  }
}

当执行抛出异常就走失败逻辑

//...

class Promise {
  //...

    try {
      executor(resolve, reject)
    } catch (e) {// 捕获到异常就走失败逻辑
      reject(e)
    }
  }
  //...
}

Promise 成功时执行成功回调 onFulfilled 传入成功值, 失败时执行失败回调 onRejected 传入失败值

//...

class Promise {
  // ...

  then(onFulfilled, onRejected) {
    if (this.status === ENUM.FULFILLED) {
      onFulfilled(this.value)
    }
    if (this.status === ENUM.REJECTED) {
      onRejected(this.reason)
    }
  }
}

异步逻辑

利用发布订阅实现异步逻辑

//...

class Promise {
  constructor(executor) {
    this.status = ENUM.PENDING
    this.onResolvedCallbacks = []// 成功的回调队列
    this.onRejectedCallbacks = []// 失败的回调队列

    const resolve = value => {
      if (this.status === ENUM.PENDING) {
        this.status = ENUM.FULFILLED
        this.value = value
        this.onResolvedCallbacks.forEach(fn => fn())// 发布成功事件,执行成功的回调队列
      }
    }
    const reject = reason => {
      if (this.status === ENUM.PENDING) {
        this.status = ENUM.REJECTED
        this.reason = reason
        this.onRejectedCallbacks.forEach(fn => fn())// 发布失败事件,执行失败的回调队列
      }
    }

    //...
  }
  then(onFulfilled, onRejected) {
    //...

    if (this.status === ENUM.PENDING) {
      // resolve 或 reject 方法还未被调用
      this.onResolvedCallbacks.push(() => {
        onFulfilled(this.value)// 订阅成功事件
      })
      this.onRejectedCallbacks.push(() => {
        onRejected(this.reason)// 订阅失败事件
      })
    }
  }
}

then 必须返回一个 Promise(promise2), promise2 需要根据 promise1 回调返回的结果(x)确定状态, 通过承诺解决程序(resolvePromise)解决 x 和 promise2 状态的问题

//...

const resolvePromise = (x, promise2, resolve, reject) => {
  // TODO:
}
class Promise {

  //...

  then(onFulfilled, onRejected) {
       // 调用 then 方法 创建一个新的 promise
    let promise2 = new Promise((resolve, reject) => {
      if (this.status === ENUM.FULFILLED) {
        // 异步逻辑保证 promise2 已被创建
        setTimeout(() => {
          let x = onFulfilled(this.value)
          resolvePromise(x, promise2, resolve, reject)
        }, 0)
      }
      if (this.status === ENUM.REJECTED) {
        setTimeout(() => {
          let x = onRejected(this.reason)
          resolvePromise(x, promise2, resolve, reject)
        }, 0)
      }
      if (this.status === ENUM.PENDING) {
        this.onResolvedCallbacks.push(() => {
          setTimeout(() => {
            let x = onFulfilled(this.value)
            resolvePromise(x, promise2, resolve, reject)
          }, 0)
        })
        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            let x = onRejected(this.reason)
            resolvePromise(x, promise2, resolve, reject)
          }, 0)
        })
      }
    })

    return promise2
  }
}

promise.then 回调抛出异常, 则走 promise2 的失败逻辑

//ENUM...

class Promise {
  //constructor...

  then(onFulfilled, onRejected) {
    let promise2 = new Promise((resolve, reject) => {
      if (this.status === ENUM.FULFILLED) {
        setTimeout(() => {
          try {
            let x = onFulfilled(this.value)
            resolvePromise(x, promise2, resolve, reject)
          } catch (e) {// 捕获异常走失败逻辑
            reject(e)
          }
        }, 0)
      }
      if (this.status === ENUM.REJECTED) {
        setTimeout(() => {
          try {
            let x = onRejected(this.reason)
            resolvePromise(x, promise2, resolve, reject)
          } catch (e) {
            reject(e)
          }
        }, 0)
      }
      if (this.status === ENUM.PENDING) {
        this.onResolvedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onFulfilled(this.value)
              resolvePromise(x, promise2, resolve, reject)
            } catch (e) {
              reject(e)
            }
          }, 0)
        })
        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onRejected(this.reason)
              resolvePromise(x, promise2, resolve, reject)
            } catch (e) {
              reject(e)
            }
          }, 0)
        })
      }
    })

    return promise2
  }
}

如果 promise2 和 x 指向同一个对象,则走失败逻辑 将 TypeError 作为 reason

//ENUM...

const resolvePromise = (x, promise2, resolve, reject) => {
  if (x === promise2) {
    reject(new TypeError('TypeError: Chaining cycle detected for promise #<Promise>'))
  }
}

//Promise...

x 不是 object 或 function 则走成功逻辑, 将 x 作为value

//ENUM...

const resolvePromise = (x, promise2, resolve, reject) => {
  if (x === promise2) {
    reject(new TypeError('TypeError: Chaining cycle detected for promise #<Promise>'))
  }
  if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
    //TODO:
  } else {
    resolve(x)
  }
}

//Promise...

设置 let then = x.then 若报错则走失败逻辑, 将错误作为 reason

//ENUM...

const resolvePromise = (x, promise2, resolve, reject) => {
  if (x === promise2) {
    reject(new TypeError('TypeError: Chaining cycle detected for promise #<Promise>'))
  }
  if (typeof x === 'object' && x !== null || typeof x === 'function') {
    try {
      let then = x.then
    } catch (e) {
      reject(e)
    }
  } else {
    resolve(x)
  }
}

//Promise...

如果 then 不是 function 则走成功逻辑 x 作为 value

//ENUM...

const resolvePromise = (x, promise2, resolve, reject) => {
  if (x === promise2) {
    reject(new TypeError('TypeError: Chaining cycle detected for promise #<Promise>'))
  }
  if (typeof x === 'object' && x !== null || typeof x === 'function') {
    try {
      let then = x.then
      if (typeof then === 'function') {
        //TODO: promise
      } else {
        resolve(x)
      }
    } catch (e) {
      reject(e)
    }
  } else {
    resolve(x)
  }
}

//Promise...

如果 then 是 function 则通过 call(x) 调用 then, 此时已认定 x 为 promise,通过订阅成功和失败两个事件获取 x 的状态

//ENUM...

const resolvePromise = (x, promise2, resolve, reject) => {
  if (x === promise2) {
    reject(new TypeError('TypeError: Chaining cycle detected for promise #<Promise>'))
  }
  if (typeof x === 'object' && x !== null || typeof x === 'function') {
    try {
      let then = x.then
      if (typeof then === 'function') {
        then.call(x, y => {
          // y 可能是一个promise
          // 递归解析 y 的值
          resolvePromise(y, promise2, resolve, reject)
        }, r => {
          reject(r)
        })
      } else {
        resolve(x)
      }
    } catch (e) {
      reject(e)
    }
  } else {
    resolve(x)
  }
}

//Promise...

确保 reject 和 resolve 只有一个被调用

//ENUM...

const resolvePromise = (x, promise2, resolve, reject) => {
  if (x === promise2) {
    reject(new TypeError('TypeError: Chaining cycle detected for promise #<Promise>'))
  }
  if (typeof x === 'object' && x !== null || typeof x === 'function') {
    let called
    try {
      let then = x.then
      if (typeof then === 'function') {
        then.call(x, y => {
          if (called) {// 被调用过就返回
            return
          }
          called = true
          resolvePromise(y, promise2, resolve, reject)
        }, r => {
          if (called) {
            return
          }
          called = true
          reject(r)
        })
      } else {
        resolve(x)
      }
    } catch (e) {
      if (called) {
        return
      }
      called = true
      reject(e)
    }
  } else {
    resolve(x)
  }
}

//Promise...

主要功能代码

const ENUM = {
  PENDING: 'PENDING',// 等待
  FULFILLED: 'FULFILLED',// 成功
  REJECTED: 'REJECTED'// 失败
}

const resolvePromise = (x, promise2, resolve, reject) => {
  if (x === promise2) {
    reject(new TypeError('TypeError: Chaining cycle detected for promise #<Promise>'))
  }
  if (typeof x === 'object' && x !== null || typeof x === 'function') {
    let called
    try {
      let then = x.then
      if (typeof then === 'function') {
        then.call(x, y => {
          if (called) {
            return
          }
          called = true
          // y 可能是一个promise
          // 递归解析 y 的值
          resolvePromise(y, promise2, resolve, reject)
        }, r => {
          if (called) {
            return
          }
          called = true
          reject(r)
        })
      } else {
        resolve(x)
      }
    } catch (e) {
      if (called) {
        return
      }
      called = true
      reject(e)
    }
  } else {
    resolve(x)
  }
}

class Promise {
  /**
   * @param {function} executor - 执行器
   */
  constructor(executor) {
    this.status = ENUM.PENDING// 默认等待态
    this.onResolvedCallbacks = []// 成功的回调队列
    this.onRejectedCallbacks = []// 失败的回调队列

    const resolve = value => {// 成功回调
      if (this.status === ENUM.PENDING) {// 处于 pending 状态才能改变状态
        this.status = ENUM.FULFILLED
        this.value = value
        this.onResolvedCallbacks.forEach(fn => fn())// 执行成功的回调队列
      }
    }
    const reject = reason => {// 失败回调
      if (this.status === ENUM.PENDING) {
        this.status = ENUM.REJECTED
        this.reason = reason
        this.onRejectedCallbacks.forEach(fn => fn())// 执行失败的回调队列
      }
    }

    try {
      executor(resolve, reject)
    } catch (e) {// 捕获到异常就走失败逻辑
      reject(e)
    }
  }
  then(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v
    onRejected = typeof onRejected === 'function' ? onRejected : err => {
      throw err
    }
    // 调用 then 方法 创建一个新的 promise
    let promise2 = new Promise((resolve, reject) => {
      // 根据 x 的状况判断是调用 resolve 还是 reject
      if (this.status === ENUM.FULFILLED) {
        // 异步逻辑保证 promise2 已被创建
        setTimeout(() => {
          try {
            let x = onFulfilled(this.value)
            resolvePromise(x, promise2, resolve, reject)
          } catch (e) {
            reject(e)
          }
        }, 0)
      }
      if (this.status === ENUM.REJECTED) {
        setTimeout(() => {
          try {
            let x = onRejected(this.reason)
            resolvePromise(x, promise2, resolve, reject)
          } catch (e) {
            reject(e)
          }
        }, 0)
      }
      if (this.status === ENUM.PENDING) {
        // resolve 或 reject 方法还未被调用
        this.onResolvedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onFulfilled(this.value)
              resolvePromise(x, promise2, resolve, reject)
            } catch (e) {
              reject(e)
            }
          }, 0)
        })
        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onRejected(this.reason)
              resolvePromise(x, promise2, resolve, reject)
            } catch (e) {
              reject(e)
            }
          }, 0)
        })
      }
    })

    return promise2
  }
}

Promise方法实现

catch 方法

//ENUM...
//resolvePromise...

class Promise {
  //constructor...
  //then...
  catch(errCallback) {
    return this.then(null, errCallback)
  }
}

resolve 方法 和 reject 方法

//ENUM...
//resolvePromise...

class Promise {
  constructor(executor) {

    //...

    const resolve = value => {// 成功回调

      // 如果 value 是一个 promise,需要进行递归解析
      if (value instanceof Promise) {
        return value.then(resolve, reject)
      }

      //...
    }
  }
  //then...
  //catch...
  static resolve(value) {
    return new Promise(resolve => {
      resolve(value)
    })
  }
  static reject(reason) {
    return new Promise((resolve, reject) => {
      reject(reason)
    })
  }
}

finally 方法

//ENUM...
//resolvePromise...

class Promise {
  //constructor...
  //then...
  //catch...
  finally(callback) {
    return this.then(value => {
      return Promise.resolve(callback()).then(() => value)
    }, err => {
      return Promise.resolve(callback()).then(() => {
        throw err
      })
    })
  }
  //resolve
  //reject
}

all 方法

//ENUM...
//resolvePromise...

class Promise {
  //constructor...
  //then...
  //catch...
  //finally...
  //resolve...
  //reject...
  static all(promises) {
    return new Promise((resolve, reject) => {
      let resultArr = []
      let orderIndex = 0
      const processResultByKey = (value, index) => {
        resultArr[index] = value
        if (++orderIndex === promises.length) {
          resolve(resultArr)
        }
      }
      for (let i = 0, len = promises.length; i < len; i++ ) {
        let current = promises[i]
        if (isPromise(current)) {
          // 如果其中一个Promise则直接调用返回的promise的reject
          current.then(val => {
            processResultByKey(val, i)
          }, reject)
        } else {
          processResultByKey(current, i)
        }
      }
    })
  }
}

race 方法

//ENUM...
//resolvePromise...

class Promise {
  //constructor...
  //then...
  //catch...
  //finally...
  //resolve...
  //reject...
  //all...
  static race(promises) {
    return new Promise((resolve, reject) => {
      // 谁先返回 就用谁
      // 一起执行就是 for
      for (let i = 0; i < promises.length; i++) {
        let current = promises[i]
        if (isPromise(current)) {
          current.then(resolve, reject)
        } else {
          resolve(current)
        }
      }
    })
  }
}

完整代码

const ENUM = {
  PENDING: 'PENDING',// 等待
  FULFILLED: 'FULFILLED',// 成功
  REJECTED: 'REJECTED'// 失败
}

const isPromise = value => {
  if (typeof value === 'object' && value !== null || typeof value === 'function') {
    return typeof value.then === 'function'
  }
}

const resolvePromise = (x, promise2, resolve, reject) => {
  if (x === promise2) {
    reject(new TypeError('TypeError: Chaining cycle detected for promise #<Promise>'))
  }
  if (typeof x === 'object' && x !== null || typeof x === 'function') {
    let called
    try {
      let then = x.then
      if (typeof then === 'function') {
        then.call(x, y => {
          if (called) {
            return
          }
          called = true
          // y 可能是一个promise
          // 递归解析 y 的值
          resolvePromise(y, promise2, resolve, reject)
        }, r => {
          if (called) {
            return
          }
          called = true
          reject(r)
        })
      } else {
        resolve(x)
      }
    } catch (e) {
      if (called) {
        return
      }
      called = true
      reject(e)
    }
  } else {
    resolve(x)
  }
}

class Promise {
  /**
   * @param {function} executor - 执行器
   */
  constructor(executor) {
    this.status = ENUM.PENDING// 默认等待态
    this.onResolvedCallbacks = []// 成功的回调队列
    this.onRejectedCallbacks = []// 失败的回调队列

    const reject = reason => {// 失败回调
      if (this.status === ENUM.PENDING) {
        this.status = ENUM.REJECTED
        this.reason = reason
        this.onRejectedCallbacks.forEach(fn => fn())// 执行失败的回调队列
      }
    }
    const resolve = value => {// 成功回调

      // 如果 value 是一个 promise,需要进行递归解析
      if (value instanceof Promise) {
        return value.then(resolve, reject)
      }

      if (this.status === ENUM.PENDING) {// 处于 pending 状态才能改变状态
        this.status = ENUM.FULFILLED
        this.value = value
        this.onResolvedCallbacks.forEach(fn => fn())// 执行成功的回调队列
      }
    }

    try {
      executor(resolve, reject)
    } catch (e) {// 捕获到异常就走失败逻辑
      reject(e)
    }
  }
  then(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v
    onRejected = typeof onRejected === 'function' ? onRejected : err => {
      throw err
    }
    // 调用 then 方法 创建一个新的 promise
    let promise2 = new Promise((resolve, reject) => {
      // 根据 x 的状况判断是调用 resolve 还是 reject
      if (this.status === ENUM.FULFILLED) {
        // 异步逻辑保证 promise2 已被创建
        setTimeout(() => {
          try {
            let x = onFulfilled(this.value)
            resolvePromise(x, promise2, resolve, reject)
          } catch (e) {
            reject(e)
          }
        }, 0)
      }
      if (this.status === ENUM.REJECTED) {
        setTimeout(() => {
          try {
            let x = onRejected(this.reason)
            resolvePromise(x, promise2, resolve, reject)
          } catch (e) {
            reject(e)
          }
        }, 0)
      }
      if (this.status === ENUM.PENDING) {
        // resolve 或 reject 方法还未被调用
        this.onResolvedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onFulfilled(this.value)
              resolvePromise(x, promise2, resolve, reject)
            } catch (e) {
              reject(e)
            }
          }, 0)
        })
        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onRejected(this.reason)
              resolvePromise(x, promise2, resolve, reject)
            } catch (e) {
              reject(e)
            }
          }, 0)
        })
      }
    })

    return promise2
  }
  catch(errCallback) {
    return this.then(null, errCallback)
  }
  finally(callback) {
    return this.then(value => {
      return Promise.resolve(callback()).then(() => value)
    }, err => {
      return Promise.resolve(callback()).then(() => {
        throw err
      })
    })
  }
  static resolve(value) {
    return new Promise(resolve => {
      resolve(value)
    })
  }
  static reject(reason) {
    return new Promise((resolve, reject) => {
      reject(reason)
    })
  }
  static all(promises) {
    return new Promise((resolve, reject) => {
      let resultArr = []
      let orderIndex = 0
      const processResultByKey = (value, index) => {
        resultArr[index] = value
        if (++orderIndex === promises.length) {
          resolve(resultArr)
        }
      }
      for (let i = 0, len = promises.length; i < len; i++ ) {
        let current = promises[i]
        if (isPromise(current)) {
          // 如果其中一个Promise则直接调用返回的promise的reject
          current.then(val => {
            processResultByKey(val, i)
          }, reject)
        } else {
          processResultByKey(current, i)
        }
      }
    })
  }
  static race(promises) {
    return new Promise((resolve, reject) => {
      // 谁先返回 就用谁
      // 一起执行就是 for
      for (let i = 0; i < promises.length; i++) {
        let current = promises[i]
        if (isPromise(current)) {
          current.then(resolve, reject)
        } else {
          resolve(current)
        }
      }
    })
  }
}

Promises/A+ Compliance Test

测试工具

// promise.js
//Promise...
// promises-aplus-tests
Promise.deferred = function() {
  let dfd = {}
  dfd.promise = new Promise((resolve, reject) => {
    dfd.resolve = resolve
    dfd.reject = reject
  })
  return dfd
}
Promise.resolved = function(value) {
  return Promise.resolve(value)
}
Promise.rejected = function(reason) {
  return Promise.reject(reason)
}
module.exports = Promise