手写 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
相关推荐
正小安27 分钟前
如何在微信小程序中实现分包加载和预下载
前端·微信小程序·小程序
_.Switch2 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
一路向前的月光2 小时前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js
长路 ㅤ   2 小时前
vite学习教程06、vite.config.js配置
前端·vite配置·端口设置·本地开发
长路 ㅤ   2 小时前
vue-live2d看板娘集成方案设计使用教程
前端·javascript·vue.js·live2d
Fan_web2 小时前
jQuery——事件委托
开发语言·前端·javascript·css·jquery
安冬的码畜日常2 小时前
【CSS in Depth 2 精译_044】第七章 响应式设计概述
前端·css·css3·html5·响应式设计·响应式
莹雨潇潇3 小时前
Docker 快速入门(Ubuntu版)
java·前端·docker·容器
Jiaberrr3 小时前
Element UI教程:如何将Radio单选框的圆框改为方框
前端·javascript·vue.js·ui·elementui
Tiffany_Ho4 小时前
【TypeScript】知识点梳理(三)
前端·typescript