手动实现 Promise

196 阅读6分钟

手动实现 Promise

Promise/A+

Promises/A+标准是一个开放、健全且通用的 JavaScript Promise 标准,符合该标准,我们即可提供给他人使用

基本使用

new Promise((resolve, reject) => {
  const random = Math.random()
  if (random > 0.5) {
    resolve('success')
  } else {
    reject('failure')
  }
}).then(value => {
  console.log('value', value)
}, e => {
  console.log('e', e)
}).catch(error => {
  console.log('catch error', error)
})

核心方法

  • Promise 有三种状态,进行中 pending,已完成 fulfilled,已失败 rejected
  • Promise 是一个构造函数,实例化时传入一个函数作为处理器,处理器立即执行
    • 处理器函数有两个参数 resolve 和 reject 分别将结果转变为成功和失败
    • Promise 对象执行的成功结果由 resolve 传递,失败原因由 reject 传递
  • Promise 的原型上定义着 then 方法,then 方法接受两个参数,分别在成功和失败时执行

由以上写出基本结构

class PromiseA {
  constructor(executor) {
    // 状态
    this.state = 'pending'
    // 成功值
    this.value = undefined
    // 失败原因
    this.reason = undefined
    const resolve = () => {}
    const reject = () => {}
    try {
      // 处理器立即执行
      executor(resolve, reject)
    } catch (error) {
      reject(error)
    }
  }
  then(onFulfilled, onRejected) {}
}

resolve 和 reject 回调实现

Promise/A+规定,由 pending 改变为 fulfilledrejected后不可再次更改,因此在更新状态时需判断,如果是等待态才可更新

const resolve = value => {
  if (this.state === 'pending') {
    this.state = 'fulfilled'
    this.value = value
  }
}
const reject = reason => {
  if (this.state === 'pending') {
    this.state = 'rejected'
    this.reason = reason
  }
}

实现 then 方法

then 的参数非必填,Promises/A+规范中定义非函数类型可忽略

class PromiseA {
  then(onFulfilled, onRejected) {
    if (this.state === 'fulfilled') {
      if (typeof onFulfilled === 'function') {
        onFulfilled(this.value)
      }
    }
    if (this.state === 'rejected') {
      if (typeof onRejected === 'function') {
        onRejected(this.reason)
      }
    }
  }
}

实现异步

const p1 = new PromiseA((resolve, reject) => {
  setTimeout(() => {
    resolve(1);
  },1000);
})

p.then(data => console.log(data)) // 无输出

then 方法在执行时状态仍是pending,故接下来在 then 中添加,在等待态时将方法寄存在队列,当状态发生改变时再执行

class PromiseA {
  constructor() {
    // ...
    // 保存成功回调
    this.onFulfilledCallbacks = []
    // 保存失败回调
    this.onRejectedCallbacks = []
    // ...
  }
}

修改 then 方法

class PromiseA {
  then(onFulfilled, onRejected) {
    // 等待态,此时异步代码还未走完,将回调放入队列
    if (this.state === 'pending') {
      if (typeof onFulfilled === 'function') {
        this.onFulfilledCallbacks.push(() => {
          onFulfilled(this.value)
        })
      }
      if (typeof onRejected === 'function') {
        this.onRejectedCallbacks.push(() => {
          onRejected(this.reason)
        })
      }
    }
    // ...
  }
}

在状态改变时执行队列中的函数

class PromiseA {
  constructor(executor) {
    const resolve = value => {
      if (this.state === 'pending') {
        this.state = 'fulfilled'
        this.value = value
        this.onFulfilledCallbacks.forEach(cb => cb())
      }
    }
    const reject = reason => {
      if (this.state === 'pending') {
        this.state = 'rejected'
        this.reason = reason
        this.onRejectedCallbacks.forEach(cb => cb())
      }
    }
  }
}

实现链式调用

PromiseA+对 then 的规范:

  • 首先 then 方法必须返回一个 Promise 对象(划重点)
  • 如果 then 方法中返回的是一个普通值(如 NumberString等)就使用此值包装成一个新的 Promise 对象返回
  • 如果 then 方法中出现异常,则调用失败态方法(reject)跳转到下一个 then 的 onRejected
  • 如果 then 方法没有传入任何回调,则继续向下传递(值穿透)
  • 如果 then 方法中返回了一个 Promise 对象,那就以这个对象为准,返回它的结果
class PromiseA {
  then(onFulfilled, onRejected) {
    // 如果 then 方法没有传入任何回调,将其变成普通值返回
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
    onRejected =
      typeof onRejected === 'function'
        ? onRejected
        : error => {
            throw error
          }
    // then 方法必须返回一个 Promise 对象
    // 实例化一个Promise,把原来写的代码放到该实例的处理器函数中
    const p2 = new PromiseA((resolve, reject) => {
      if (this.state === 'pending') {
        this.onFulfilledCallbacks.push(() => {
          // 使用 queueMicrotask 将函数添加到微任务执行队列
          queueMicrotask(() => {
            try {
              const x = onFulfilled(this.value)
              resolvePromise(p2, x, resolve, reject)
            } catch (error) {
              reject(error)
            }
          })
        })
        this.onRejectedCallbacks.push(() => {
          queueMicrotask(() => {
            try {
              const x = onRejected(this.reason)
              resolvePromise(p2, x, resolve, reject)
            } catch (error) {
              reject(error)
            }
          })
        })
      }
      if (this.state === 'fulfilled') {
        queueMicrotask(() => {
          try {
            const x = onFulfilled(this.value)
            resolvePromise(p2, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        })
      }
      if (this.state === 'rejected') {
        queueMicrotask(() => {
          try {
            const x = onRejected(this.reason)
            resolvePromise(p2, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        })
      }
    })
    return p2
  }
}
/**
 * 解析then返回值与新Promise对象
 * @param {Object} 新的Promise对象,就是我们创建的p2实例
 * @param {x} 上一个then的返回值
 * @param {function} resolve p2处理器函数的resolve
 * @param {function} reject p2处理器函数的reject
 */
const resolvePromise = (p2, x, resolve, reject) => {
  // 解决循环引用报错
  if (p2 === x) {
    // reject报错
    reject(new TypeError('请避免Promise循环引用'))
  }

  // 对返回值 x 分情况处理
  // x 是对象或函数
  if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
    // 定义状态-防止多次调用
    let called = false
    try {
      const { then } = x
      // x 有then方法即可认为是 Promise 对象
      if (typeof then === 'function') {
        // 执行then 使用call传递this 第一个参数是this 后面是成功的回调 和 失败的回调
        then.call(
          x,
          y => {
            // 成功和失败只能调用一个
            if (called) return
            called = true
            // 递归调用,传入y若是Promise对象,继续循环
            resolvePromise(p2, y, resolve, reject)
          },
          error => {
            // 成功和失败只能调用一个
            if (called) return
            called = true
            reject(error)
          }
        )
      } else {
        resolve(x)
      }
    } catch {
      if (called) return
      called = true
      reject(x)
    }
  } else {
    // 普通类型的值直接返回
    resolve(x)
  }
}

其他方法

其他方法如catch,all,race均较为简单,此处附上完整代码

/**
 * 定义状态常量
 */
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

/**
 * 判断是否为可迭代类型
 */
const isIterable = iterator => {
  return (
    iterator !== null &&
    iterator !== undefined &&
    typeof iterator[Symbol.iterator] === 'function'
  )
}

/**
 * 是否为Map
 */
const isMap = map => {
  return toString.call(map) === '[object Map]'
}

/**
 * 是否为Set
 */
const isSet = set => {
  return toString.call(set) === '[object Set]'
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
class PromiseA {
  constructor(exec) {
    this.state = PENDING
    this.value = undefined
    this.reason = undefined
    this.onFulfilledCallbacks = []
    this.onRejectedCallback = []
    const resolve = value => {
      if (this.state === PENDING) {
        this.state = FULFILLED
        this.value = value
        this.onFulfilledCallbacks.forEach(cb => cb())
      }
    }
    const reject = reason => {
      if (this.state === PENDING) {
        this.state = REJECTED
        this.reason = reason
        this.onRejectedCallback.forEach(cb => cb())
      }
    }
    try {
      exec(resolve, reject)
    } catch (error) {
      reject(error)
    }
  }
  then(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
    onRejected =
      typeof onRejected === 'function'
        ? onRejected
        : error => {
            throw error
          }
    const p2 = new PromiseA((resolve, reject) => {
      if (this.state === PENDING) {
        this.onFulfilledCallbacks.push(() => {
          queueMicrotask(() => {
            try {
              const x = onFulfilled(this.value)
              resolvePromise(p2, x, resolve, reject)
            } catch (error) {
              reject(error)
            }
          })
        })
        this.onRejectedCallback.push(() => {
          queueMicrotask(() => {
            try {
              const x = onRejected(this.reason)
              resolvePromise(p2, x, resolve, reject)
            } catch (error) {
              reject(error)
            }
          })
        })
      }
      if (this.state === FULFILLED) {
        queueMicrotask(() => {
          try {
            const x = onFulfilled(this.value)
            resolvePromise(p2, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        })
      }
      if (this.state === REJECTED) {
        queueMicrotask(() => {
          try {
            const x = onRejected(this.reason)
            resolvePromise(p2, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        })
      }
    })
    return p2
  }
  static resolve(value) {
    return new PromiseA(resolve => {
      resolve(value)
    })
  }
  static reject(reason) {
    return new PromiseA((_, reject) => {
      reject(reason)
    })
  }
  /**
   * 全部完成
   */
  static all(promises) {
    return new PromiseA((resolve, reject) => {
      const results = []
      if (!isIterable(promises)) {
        throw new TypeError(`${typeof promises} is not iterable`)
      }
      if (isMap(promises)) {
        resolve(Array.from(promises))
      }
      if (isSet(promises)) {
        promises = Array.from(promises)
      }
      if (!promises.length) {
        resolve(results)
      }
      // 每完成一个promise,count 减 1,为0时返回结果
      if (typeof promises === 'string') {
        promises = promises.split('')
      }
      let count = promises.length
      promises.forEach((promise, index) => {
        if (promise instanceof this) {
          promise.then(
            value => {
              results[index] = value
              count -= 1
              if (!count) resolve(results)
            },
            reason => {
              reject(reason)
            }
          )
        } else {
          results[index] = promise
          count -= 1
          if (!count) resolve(results)
        }
      })
    })
  }
  /**
   * 等待所有promise结果
   */
  static allSettled(promises) {
    return new PromiseA(resolve => {
      const results = []
      if (!isIterable(promises)) {
        throw new TypeError(`${typeof promises} is not iterable`)
      }
      if (isMap(promises) || isSet(promises)) {
        promises = Array.from(promises)
      }
      if (!promises.length) {
        resolve(results)
      }
      if (typeof promises === 'string') {
        promises = promises.split('')
      }
      let count = promises.length
      promises.forEach((promise, index) => {
        if (promise instanceof this) {
          promise.then(
            value => {
              results[index] = {
                status: FULFILLED,
                value
              }
              count -= 1
              if (!count) resolve(results)
            },
            reason => {
              results[index] = {
                status: REJECTED,
                reason
              }
              count -= 1
              if (!count) resolve(results)
            }
          )
        } else {
          results[index] = {
            status: FULFILLED,
            value: promise
          }
          count -= 1
          if (!count) resolve(results)
        }
      })
    })
  }
  /**
   * 任意一个有结果,无论成功与否
   */
  static race(promises) {
    return new PromiseA((resolve, reject) => {
      if (!isIterable(promises)) {
        throw new TypeError(`${typeof promises} is not iterable`)
      }
      if (isMap(promises) || isSet(promises)) {
        promises = Array.from(promises)
      }
      if (typeof promises === 'string') {
        promises = promises.split('')
      }
      promises.forEach(promise => {
        if (promise instanceof this) {
          promise.then(
            value => {
              resolve(value)
            },
            reason => {
              reject(reason)
            }
          )
        } else {
          resolve(promise)
        }
      })
    })
  }
  /**
   * 任意一个成功即可
   */
  static any(promises) {
    return new PromiseA((resolve, reject) => {
      const rejects = []
      if (!isIterable(promises)) {
        throw new TypeError(`${typeof promises} is not iterable`)
      }
      if (isMap(promises) || isSet(promises)) {
        promises = Array.from(promises)
      }
      if (!promises.length) {
        reject(new AggregateError('', 'All promises were rejected'))
      }
      if (typeof promises === 'string') {
        promises = promises.split('')
      }
      let count = promises.length
      promises.forEach((promise, index) => {
        if (promise instanceof this) {
          promise.then(
            value => {
              resolve(value)
            },
            reason => {
              rejects[index] = reason
              count -= 1
              if (!count) {
                reject(new AggregateError('', 'All promises were rejected'))
              }
            }
          )
        } else {
          resolve(promise)
        }
      })
    })
  }
  catch(onRejected) {
    return this.then(null, onRejected)
  }
  /**
   * finally 表示不是最终的意思,而是无论如何都会执行的意思
   * 如果返回一个 promise 会等待这个 promise 也执行完毕
   * 上一次成功且返回 promise 成功,采用上一次的结果
   * 上一次失败或返回 promise 失败,采用失败的结果
   * 上一次失败且返回 promise 失败,采用返回promise失败的结果
   * 如返回的不是promise,则其返回值没有意义
   */
  finally(callback) {
    return this.then(
      value => {
        const p1 = callback()
        if (p1 instanceof PromiseA) {
          return p1.then(
            () => {
              return value
            },
            error2 => {
              throw error2
            }
          )
        } else {
          return value
        }
      },
      error1 => {
        const p1 = callback()
        if (p1 instanceof PromiseA) {
          return p1.then(
            () => {
              throw error1
            },
            error2 => {
              throw error2
            }
          )
        } else {
          throw error1
        }
      }
    )
  }
}

/**
 * 解析结果
 */
const resolvePromise = (p2, x, resolve, reject) => {
  if (p2 === x) {
    reject(new TypeError('请避免promise循环引用'))
  }
  let called = false
  if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
    try {
      const { then } = x
      if (typeof then === 'function') {
        then.call(
          x,
          y => {
            if (called) return
            called = true
            resolvePromise(p2, y, resolve, reject)
          },
          error => {
            if (called) return
            called = true
            reject(error)
          }
        )
      } else {
        resolve(x)
      }
    } catch (error) {
      if (called) return
      called = true
      reject(error)
    }
  } else {
    resolve(x)
  }
}

代码测试

将以下代码添加到文件中

PromiseA.deferred = function () {
  const defer = {}
  defer.promise = new PromiseA((resolve, reject) => {
    defer.resolve = resolve
    defer.reject = reject
  })
  return defer
}

try {
  module.exports = PromiseA
} catch (error) {
  console.error(error)
}

安装依赖,执行命令即可,测试通过后即算完成

npm install promises-aplus-tests -D
npx promises-aplus-tests Promise.js

参考链接