手写 Promise 及 Promise 的静态方法

手写 Promise A+ 规范

首先 基于 Promise 的状态我们定义几个常见的常量

js 复制代码
// Promise 有两个阶段, 三种状态
const PENDING = 'pending' //待定状态
const FULFILLED = 'fulfilled' //已兑现 状态
const REJECTED = 'rejected' //已拒绝 状态

const SETTLED = 'settled' // 已决阶段
const UNSETTLED = 'unsettled' // 未决阶段

注意: 如果一个 Promise 已经被兑现或拒绝,即不再处于待定状态,那么则称之为已敲定(settled) 。这意味着该 Promise 已经敲定(settled),或为了匹配另一个 Promise 的最终状态而被"锁定(lock-in)",进一步解决或拒绝它都没有影响

基于 Promise 是 通过 new 关键字创建的, 创建自己的 Promise 类 RPromise

js 复制代码
class RPromise {
  /**
   * Promise 构造函数
   * @param {Function} executor 任务执行器
   */
  constructor(executor) {
    this._state = PENDING //状态
    this._value = undefined // 返回包装的值
    this._handlers = [] //处理函数任务队列
    try {
      executor(this._resolve.bind(this), this._reject.bind(this))
    } catch (error) {
      this._reject(error)
      console.log('Error:', error)
    }
  }
}

我们先实现最简单的如何改变 Promise 的状态

js 复制代码
   /**
   *  设置任务到 fulfilled
   */
  _resolve(data) {
    if (this._state === PENDING) {
      this._changeState(FULFILLED, data)
    }
  }

  _changeState(newState, value) {
    if (this._state !== PENDING) return
    this._state = newState
    this._value = value
    this._runHandlers()
  }

  /**
   * 设置任务到 rejected
   */
  _reject(reason) {
    if (this._state === PENDING) {
      this._changeState(REJECTED, reason)
    }
  }

接下来 基于 Promise A+ 规范来实现 then 方法

js 复制代码
  /**
   *  Promise A+ .then  方法
   * @param {Function} onFulfilled
   * @param {Function} onRejected
   */
  then(onFulfilled, onRejected) {
    // onFulfilled =
    //   typeof onFulfilled === 'function' ? onFulfilled : () => onFulfilled
    // onRejected =
    //   typeof onRejected === 'function'
    //     ? onRejected
    //     : () => {
    //         throw onRejected
    //       }
    return new RPromise((resolve, reject) => {
      this._pushHandles(onFulfilled, FULFILLED, resolve, reject)
      this._pushHandles(onRejected, REJECTED, resolve, reject)
      this._runHandlers() //执行队列
    })
  }
  
  /**
   * 根据实际情况,执行队列
   */
  _runHandlers() {
    if (this._state === PENDING) return
    if (!Array.isArray(this._handlers)) return
    while (this._handlers.length) {
      const handler = this._handlers.shift()
      this._runOnceHandler(handler)
    }
  }
  
  /**
   * 向处理队列中添加一个函数
   * @param {Function} executor
   * @param {String} state
   * @param {Function} resolve 让 then 函数 返回的 Promise 成功
   * @param {Function} reject 让 then 函数返回的 Promise 失败
   */
  _pushHandles(executor, state, resolve, reject) {
    if (executor && typeof executor === 'function') {
      this._handlers.push({
        executor,
        state,
        resolve,
        reject,
      })
    }
  }
  
/**
 * 判断是否为 Promise
 * @param {*} obj
 * @returns
 */
function isPromise(obj) {
  if (obj instanceof Promise) return true
  return !!(obj && typeof obj === 'object' && typeof obj.then === 'function')
}
  
  /**
   * handler 处理函数
   * @param {*} handler
   */
  _runOnceHandler(handler) {
    const { executor, state, resolve, reject } = handler
    runMicroTask(() => {
      if (this._state !== state) {
        // 状态不一致,不处理
        return
      }
      if (typeof executor !== 'function') {
        this._state === FULFILLED ? resolve(this._value) : reject(this._value)
        return
      }
      try {
        const result = executor(this._value)
        if (isPromise(result)) {
          result.then(resolve, reject)
        } else {
          resolve(result)
        }
      } catch (error) {
        reject(error)
        console.log('Error:', error)
      }
    })
  }

我们知道 promise.then 方法的回调函数是微任务队列执行,通过写一个 创建微任务队列函数来创建微任务队列

js 复制代码
/**
 * 运行一个微任务, 把传递的函数放到微任务之中
 * @param {Function} callback
 */
function runMicroTask(callback) {
  // 通过 process 来判断 node 环境, 使用 process.nextTick 实现微队列
  if (process && process.nextTick) {
    process.nextTick(callback)
  } else if (MutationObserver) {
    // 实现浏览器下的微队列
    const virElement = document.createElement('p')
    const observer = new MutationObserver(callback)
    observer.observe(virElement, {
      childList: true,
      attributes: true,
    })
    virElement.innerHTML = 'unknown'
  } else {
    setTimeout(() => {
      try {
        callback()
      } catch (e) {}
    }, 0)
  }
}  

Promise 的 catch 方法实现

js 复制代码
  /**
   * Promise catch 方法
   * @param {*} onRejected
   */
  catch(onRejected) {
    return this.then(null, onRejected)
  }

promise finally 方法实现

js 复制代码
  /**
   * finally 无论是成功或失败都会运行
   * @param {*} callback
   */
  finally(callback) {
    return this.then(
      (data) => {
        callback()
        return data
      },
      (reason) => {
        callback()
        throw reason
      }
    )
  }

promise 静态方法

resolve 方法

Promise.resolve() 静态方法将给定的值转换为一个 Promise。如果该值本身就是一个 Promise,那么该 Promise 将被返回;如果该值是一个 thenable 对象,Promise.resolve() 将调用其 then() 方法及其两个回调函数;否则,返回的 Promise 将会以该值兑现。

该函数将嵌套的类 Promise 对象(例如,一个将被兑现为另一个 Promise 对象的 Promise 对象)展平,转化为单个 Promise 对象,其兑现值为一个非 thenable 值。

js 复制代码
  /**
   * 
   * @param {*} value
   * @returns
   *
   * 1. 传递的 data 本身就是 Es6 的 Promise
   * 2. 传递的 data 是 PromiseLike (Promise A+),返回一个 新的Promise
   */
  static resolve(value) {
    // 如果该值本身就是一个 Promise(用 instanceof 判断)
    if (value instanceof RPromise || value instanceof Promise) {
      return value
    }
    return new RPromise((resolve, reject) => {
      //如果是一个类 Promise 对象
      if (isPromise(value)) {
        value.then(resolve, reject)
      } else {
      //如果为值的形式,包一个 resolve
        resolve(value)
      }
    })
  }

reject 方法

Promise.reject() 静态方法返回一个已拒绝(rejected)的 Promise 对象,拒绝原因为给定的参数。

js 复制代码
/**
 * 
 * @param {*} reason 传入失败的原因
 * @returns
 *
 */
static reject(reason) {
    return new RPromise((_, reject) => {
      reject(reason)
    })
}

注意: 下文中的所有实现都没有使用 length 属性,是因为 如果传入的是一个 iterator 迭代对象而非数组 会没有 length 属性

Promise.all 方法

Promise.all() 静态方法接受一个 Promise 可迭代对象作为输入,并返回一个 Promise。当所有输入的 Promise 都被兑现时,返回的 Promise 也将被兑现(即使传入的是一个空的可迭代对象),并返回一个包含所有兑现值的数组

js 复制代码
/**
 *
 * @param {itorator} promiseArrays
 */
RPromise.all = function (promiseArrays) {
  return new RPromise((resolve, reject) => {
    try {
      const results = []
      let count = 0
      let fulfilledCount = 0
      for (const promiseItem of promiseArrays) {
        let index = count
        count++
        RPromise.resolve(promiseItem).then((data) => {
          fulfilledCount++
          results[index] = data
          if (fulfilledCount === count) {
            // TODO:此时 promiseArr 已全部完成
            resolve(results)
          }
        }, reject)
      }
      if (count === 0) resolve(results)
    } catch (error) {
      reject(error)
    }
  })
}

promise.allSettled 方法

Promise.allSettled() 静态方法将一个 Promise 可迭代对象作为输入,并返回一个单独的 Promise。当所有输入的 Promise 都已敲定时(包括传入空的可迭代对象时),返回的 Promise 将被兑现,并带有描述每个 Promise 结果的对象数组。

js 复制代码
RPromise.allSettled = function (promiseArrays) {
  return new RPromise((resolve, reject) => {
    try {
      const ps = []
      for (const promiseItem of promiseArrays) {
        ps.push(
          RPromise.resolve(promiseItem).then(
            (value) => ({ status: FULFILLED, value: value }),
            (reason) => ({ status: REJECTED, reason: reason })
          )
        )
      }
      return RPromise.all(ps)
    } catch (error) {
      reject(error)
    }
  })

promise.race 方法

Promise.race() 静态方法接受一个 promise 可迭代对象作为输入,并返回一个 Promise。这个返回的 promise 会随着第一个 promise 的敲定而敲定。

js 复制代码
RPromise.race = function (promiseArrays) {
  return new RPromise((resolve, reject) => {
    try {
      for (const promiseItem of promiseArrays) {
        RPromise.resolve(promiseItem).then(resolve, reject)
      }
    } catch (error) {
      reject(error)
    }
  })
}

promise.any 方法

Promise.any() 静态方法将一个 Promise 可迭代对象作为输入,并返回一个 Promise。当输入的任何一个 Promise 兑现时,这个返回的 Promise 将会兑现,并返回第一个兑现的值。当所有输入 Promise 都被拒绝(包括传递了空的可迭代对象)时,它会以一个包含拒绝原因数组的 AggregateError 拒绝。

js 复制代码
RPromise.any = function (promiseArrays) {
  return new RPromise((resolve, reject) => {
    try {
      const reasonResult = []
      let count = 0
      let rejectCount = 0
      for (const promiseItem of promiseArrays) {
        count++
        RPromise.resolve(promiseItem).then(resolve, (reason) => {
          reasonResult[rejectCount] = new Error(reason)
          rejectCount++
          if (rejectCount === count) {
            reject(new AggregateError(reasonResult))
          }
        })
      }
    } catch (error) {
      reject(error)
    }
  })
}

最后使用 CommonJs 的导出 module.exports 导出我们自己的 Promise

js 复制代码
module.exports = RPromise
相关推荐
i听风逝夜6 分钟前
Web 3D地球实时统计访问来源
前端·后端
iMonster10 分钟前
React 组件的组合模式之道 (Composition Pattern)
前端
呐呐呐呐呢18 分钟前
antd渐变色边框按钮
前端
元直数字电路验证37 分钟前
Jakarta EE Web 聊天室技术梳理
前端
wadesir40 分钟前
Nginx配置文件CPU优化(从零开始提升Web服务器性能)
服务器·前端·nginx
牧码岛41 分钟前
Web前端之canvas实现图片融合与清晰度介绍、合并
前端·javascript·css·html·web·canvas·web前端
灵犀坠43 分钟前
前端面试八股复习心得
开发语言·前端·javascript
9***Y4844 分钟前
前端动画性能优化
前端
网络点点滴1 小时前
Vue3嵌套路由
前端·javascript·vue.js
牧码岛1 小时前
Web前端之Vue+Element打印时输入值没有及时更新dom的问题
前端·javascript·html·web·web前端