自己写一个 Promise [myPromise.js]

一、该自定义 Promise 可复用的核心功能

这个实现已经覆盖了原生 Promise 的核心特性,在简单场景下可以正常使用,包括:

    1. 实现了 Promise 三大状态(pending/fulfilled/rejected)的切换,且保证状态不可逆转(从 pending 只能转为 fulfilledrejected,无法回退或重复切换);
    1. 实现了 then 方法的链式调用(返回新的 Promise 实例,避免修改原实例状态),支持成功/失败回调的指定;
    1. 实现了「值穿透/错误穿透」特性,兼容 then 方法不传回调的场景;
    1. 实现了常用原型方法 catch(语法糖,基于 then 实现)和静态方法(resolve/reject/all/race);
    1. 实现了异步回调执行(用 setTimeout 模拟)和异常捕获(执行器、回调函数中的异常均可捕获并转为 rejected 状态);
    1. 支持回调函数返回普通值或 Promise 实例,正确透传后续链式调用的结果。
    1. 避免出现: 检测循环引用 和 检测循环依赖 问题。
    1. this 指向的更严谨绑定,无论以何种方式调用(如 call/apply/bind 改变 this),都能保证回调的正确执行;

二、无法替代原生 Promise 的关键差异与局限性

这个自定义实现是「简化版」,与原生 Promise 存在诸多核心差异,这些差异导致它无法在生产环境中替代原生 Promise,具体如下:

1. 缺少原生 Promise 的部分核心 API

该自定义实现未覆盖原生 Promise 的诸多实用 API,无法满足复杂场景需求:

  • 原型方法:finally()(无论 Promise 状态成功/失败,都会执行的回调,用于清理资源);
  • 静态方法:allSettled()(等待所有 Promise 完成,无论成功/失败,返回所有结果)、any()(等待第一个成功的 Promise,所有都失败才返回失败);
  • 其他特性:Promise.prototype.then 的回调参数可选性的更严谨处理、原生的 Symbol.species 支持等。
2. 回调队列处理的局限性(多 then 绑定场景)

原生 Promise 支持对同一个 Promise 实例多次调用 then 方法,所有回调都会按绑定顺序执行,且处理逻辑更严谨;
该自定义实现的 callbacks 数组虽能存储多个回调,但在极端场景下(如快速多次绑定 then、状态切换与回调绑定并发),可能存在回调执行顺序错乱或重复执行的风险,不如原生 Promise 健壮。

3. 边界场景与异常处理的健壮性不足

原生 Promise 对各种边界场景做了极致兼容,而自定义实现存在诸多遗漏:

  • 不支持 resolve/reject 传入 Promise 实例的深层透传(部分场景处理不严谨);
  • then 方法传入的非函数参数的处理不如原生严谨;
  • 不支持捕获异步执行器中的异常(当前仅能捕获同步执行器的异常,原生 Promise 可捕获异步异常并转为 rejected);
4. 原生 Promise 的性能与优化优势

原生 Promise 是浏览器/Node.js 底层内置实现(多为 C++ 编写),执行效率远高于自定义的 JavaScript 实现,尤其在大量 Promise 实例并发、复杂链式调用场景下,性能差距会非常明显;
同时,原生 Promise 会被 JavaScript 引擎做各种优化(如微任务队列的高效调度),自定义实现无法享受这些优化红利。

示例

let p = new Promise((resolve) => setTimeout(() => resolve('ok'), 1000));
// 同一个 Promise 实例绑定 3 个 then 回调
p.then(v => console.log('回调1', v));
p.then(v => console.log('回调2', v));
p.then(v => console.log('回调3', v));

原生 Promise 会按顺序输出 3 个回调结果,自定义实现虽大概率能正常执行,但底层处理逻辑未做足够的边界防护。

myPromise 代码如下:

myPromise.html:

<html>
<head><title>test promise</title></head>
<body>
<script type="text/javascript" src="myPromise.js"></script>
<script type="text/javascript">
/* 
 * 测试代码注释:批量测试 Promise.all 方法
let p1 = new myPromise((resolve, reject) =>{
  setTimeout(()=>{
    resolve('ok')
  },1000)
})
let p2 = myPromise.resolve('hello')
let p3 = myPromise.resolve('good')
let r = myPromise.all([p1,p2,p3])
console.log('r = ',r)
*/

// 测试代码:链式调用 myPromise 的 then 方法,验证自定义 Promise 功能
let p = new myPromise((resolve, reject) =>{
  // 延迟 1 秒后,将 myPromise 状态转为成功态,结果为 'ok'
  setTimeout(()=>{
    resolve('ok')
  },1000)
})

// 链式调用 then 方法,验证值穿透、链式调用和状态传递
const res = p.then(value=>{
  // throw 'FAILED' // 手动抛出异常,可测试 catch 捕获功能
  console.log(value) // 第一个 then:打印成功结果 'ok'
})
.then(value=>{
  console.log(111) // 第二个 then:值穿透,打印 111
})
.then(value=>{
  console.log(222) // 第三个 then:值穿透,打印 222
  return new myPromise(()=>{}) // 返回一个 pending 状态的 myPromise,中断后续链式调用
})
.then(value=>{
  console.log('end') // 第四个 then:由于上一个 Promise 是 pending 状态,此回调不会执行
})
.catch(reason=>console.warn(reason)) // 捕获整个链式调用中的所有异常

// 打印最终返回的 myPromise 实例(状态为 pending)
console.log(res) 
</script>

</body>
</html>

myPromise.js:

**// defer a myPromise function.
/**
 * 自定义 myPromise 构造函数
 * @param {Function} executor - 执行器函数,接收 resolve 和 reject 两个回调参数,同步执行
 */
function myPromise(executor){
  // 保存当前 myPromise 实例的 this 指向,避免在嵌套函数中丢失
  const self = this
  // 记录 myPromise 的状态,初始为 pending(等待态)
  // 状态只能从 pending -> fulfilled(成功态) 或 pending -> rejected(失败态),不可逆转
  self.PromiseState = 'pending'
  // 记录 myPromise 的结果值(成功结果或失败原因)
  self.PromiseResult = null
  // 存储回调函数队列(当 myPromise 状态未改变时,保存 then 方法传入的 onResolved/onRejected 回调)
  self.callbacks = []

  // 状态守卫:如果状态不是 pending,直接返回(确保状态不可逆转)
  if(self.PromiseState!=='pending'){ return null }

  /**
   * myPromise 成功回调函数
   * @param {*} data - 成功的结果数据
   */
  function resolve(data){
    // 再次守卫:只有 pending 状态才能转为 fulfilled,防止多次调用 resolve
    if(self.PromiseState !== 'pending') return

    // 检测循环引用:如果传入的是当前 myPromise 实例,抛出 TypeError(模拟原生 Promise 行为). 避免出现如 new myPromise((resolve) => resolve(p)) 这种 "循环 myPromise 引用" 问题
    if(data === self){
      // 改为调用 reject,抛出循环依赖错误
      reject(new TypeError('检测到Promise链式循环'))
      return
    }

    // 1. 修改 myPromise 状态为成功态(fulfilled)
    self.PromiseState = 'fulfilled'
    
    // 2. 存储成功的结果数据
    self.PromiseResult = data

    // 3. 如果存在待执行的回调函数,异步执行 onResolved 回调
    // (模拟原生 Promise 的微任务,这里用 setTimeout 模拟为宏任务,简化实现)
    if(self.callbacks && self.callbacks.length>0) {
      // 替换 setTimeout 为 queueMicrotask,创建微任务, 模拟异步执行
      queueMicrotask(()=>{ 
        self.callbacks.forEach(item=>{
          item.onResolved(data)
        }) 
      })
    }
  }

  /**
   * myPromise 失败回调函数
   * @param {*} data - 失败的原因数据
   */
  function reject(data){
    // 再次守卫:只有 pending 状态才能转为 rejected,防止多次调用 reject
    if(self.PromiseState !== 'pending') return

    // 检测循环引用:如果传入的是当前 myPromise 实例,抛出 TypeError(模拟原生 Promise 行为). 避免出现如 new myPromise((resolve) => resolve(p)) 这种 "循环 myPromise 引用" 问题
    if(data === self){
      // 改为调用 reject,抛出循环依赖错误
      reject(new TypeError('检测到Promise链式循环'))
      return
    }

    // 1. 修改 myPromise 状态为失败态(rejected)
    self.PromiseState = 'rejected'

    // 2. 存储失败的原因数据
    self.PromiseResult = data
    
    // 3. 如果存在待执行的回调函数,异步执行 onRejected 回调
    if(self.callbacks && self.callbacks.length>0) {
      // 替换 setTimeout 为 queueMicrotask,创建微任务, 模拟异步执行
      queueMicrotask(()=>{ 
        self.callbacks.forEach(item=>{
          item.onRejected(data)
        }) 
      }, 0)
    }
  }

  try{
    // 同步执行执行器函数,传入 resolve 和 reject 回调
    // 若执行器内部抛出异常,直接捕获并调用 reject 转为失败态
    executor(resolve, reject)
  }catch(e){
    reject(e)
  }

  // 没有 return,所以 new myPromise() 返回 this 对象
}

/**
 * myPromise 原型方法 then - 用于指定成功/失败的回调函数,返回一个新的 myPromise 实例(实现链式调用)
 * then 方法主要做两件事:
 * - 注册回调:将 onResolved 和 onRejected 回调函数保存到 self.callbacks 数组中
 * - 检查当前状态:
 *  - 如果状态已经是 'fullfilled',立即在 microtask 中执行 handle(onResolved)
 *  - 如果状态是 'rejected',立即执行 handle(onRejected)
 *  - 如果状态还是 'pending',只保存回调,不执行
 * 
 * @param {Function} onResolved - 成功态的回调函数
 * @param {Function} onRejected - 失败态的回调函数
 * @returns {myPromise} 新的 myPromise 实例
 * 注: 
 *  1. 原生的 then/catch 回调是微任务
 *  2. then 方法被调用时,会立即创建一个新的 myPromise 实例
 */
myPromise.prototype.then = function(onResolved, onRejected){
  // 保存当前 myPromise 实例的 this 指向
  const self = this

  // typeof undefined === 'undefined' → 进入默认处理
  // typeof null === 'object' → 进入默认处理
  // typeof function(){} === 'function' → 不进入默认处理

  // 实现值的穿透(默认回调处理):
  // 1. 若 onResolved 不是函数,设置默认成功回调,直接返回接收的结果(实现值穿透)
  if(typeof onResolved!=='function' ) { onResolved = value=>{ return value} }

  // 2. 若 onRejected 不是函数,设置默认失败回调,直接抛出接收的错误(实现错误穿透)
  if(typeof onRejected!=='function' ) { onRejected = reason=>{ throw(reason) } }

  // 返回一个新的 myPromise 实例,实现 then 方法的链式调用
  return new myPromise((resolve, reject)=>{
    /**
     * 统一处理成功/失败回调的逻辑封装
     * @param {Function} callback - 传入的 onResolved 或 onRejected 回调函数
     */
    function handle(callback){
      try{
        // 执行回调函数,传入当前 myPromise 的结果值,获取回调返回结果
        let r = callback(self.PromiseResult)

        // 检测循环依赖: 如果回调返回当前 myPromise 实例,抛出 TypeError. 避免出现如 p.then(() => p) 这种 "循环 myPromise 依赖" 问题
        if(r === self){
          throw new TypeError('检测到Promise链式循环')
        }

        // 判断回调返回结果是否为 myPromise 实例
        if(r instanceof myPromise){
          // 若返回 Promise,监听其状态变化,将结果透传给新 myPromise 的 resolve/reject
          r.then(v=>resolve(v), r=>reject(r))
        }else{
          // 若返回非 Promise,将结果作为新 myPromise 的成功值
          // 回调返回值处理:当回调函数返回 undefined、null、NaN、0、空字符串 '' 等特殊值时,原生 Promise 会将其作为有效值传递给下一个 then。
          resolve(r)
        }
      }catch(e){
        // 若回调执行过程中抛出异常,将异常作为新 myPromise 的失败原因
        reject(e)
      }
    }
    
    // 情况1:当前 myPromise 状态为成功态(fulfilled)
    if(this.PromiseState === 'fulfilled') {
      // 异步执行回调(模拟原生 Promise 微任务)
      // 替换 setTimeout 为 queueMicrotask,创建微任务, 模拟异步执行
      queueMicrotask(()=>{ 
        handle(onResolved) 
      })
    }

    // 情况2:当前 myPromise 状态为失败态(rejected)
    if(this.PromiseState==='rejected'){
      // 异步执行回调(模拟原生 Promise 微任务)
      // 替换 setTimeout 为 queueMicrotask,创建微任务, 模拟异步执行
      queueMicrotask(()=>{ 
        handle(onRejected) 
      })
    }

    // 情况3:当前 myPromise 状态为等待态(pending)
    // 此时回调无法立即执行,存入回调队列,等待状态改变后执行
    if(this.PromiseState==='pending'){
      this.callbacks.push({
        // 封装 onResolved 回调,执行时调用统一处理函数 handle
        onResolved: () => { handle(onResolved) },
        // 封装 onRejected 回调,执行时调用统一处理函数 handle
        onRejected: () => { handle(onRejected) }
      })
    }
  })
}

/**
 * myPromise 原型方法 catch - 专门用于指定失败态的回调函数(语法糖,基于 then 方法实现)
 * @param {Function} onRejected - 失败态的回调函数
 * @returns {myPromise} 新的 myPromise 实例
 * 注: 原生的 then/catch 回调是微任务
 */
myPromise.prototype.catch = function(onRejected){
  /*当前实现中 then 方法直接使用 const self = this 保存 this 指向,当使用 myPromise.prototype.then.call(null, ...) 等方式改变 this 时,会导致:
  1. this 变为 null 或其他非 myPromise 对象
  访问 this.PromiseState 或 self.PromiseResult 时出现 TypeError: Cannot read property 'PromiseState' of null
  2. 行为与原生 Promise 不一致 */

  // 检查 this 是否为 myPromise 实例,确保方法只能在 myPromise 实例上调用(模拟原生 Promise 行为)
  if(!(this instanceof myPromise)){
    throw new TypeError('myPromise.prototype.catch 调用在不兼容的接收器上')
  }

  // 调用 then 方法,第一个参数传 null,第二个参数传入失败回调,实现错误捕获
  return this.then(null, onRejected)
}

/**
 * myPromise 静态方法 resolve - 快速创建一个成功态的 myPromise 实例
 * @param {*} value - 成功的结果值(可以是普通值或 myPromise 实例)
 * @returns {myPromise} 新的 myPromise 实例
 */
myPromise.resolve = function(value){
  return new myPromise((resolve, reject)=>{
    // 若传入的值是 myPromise 实例,监听其状态变化,透传结果
    if(value instanceof myPromise){ 
      value.then(v=>resolve(v), r=>reject(r))
    }else{
      // 若传入普通值,直接创建成功态 myPromise
      resolve(value)
    }
  })
}

/**
 * myPromise 静态方法 reject - 快速创建一个失败态的 myPromise 实例
 * @param {*} value - 失败的原因值
 * @returns {myPromise} 新的 myPromise 实例
 */
myPromise.reject = function(value){
  return new myPromise((resolve, reject)=>{
    // 直接创建失败态 Promise,无论传入的值是否为 Promise,都直接作为失败原因
    reject(value)
  })
}

/**
 * myPromise 静态方法 all - 批量处理多个 myPromise 实例,全部成功才返回成功,一个失败则直接返回失败
 * @param {Array} promises - myPromise 实例数组
 * @returns {myPromise} 新的 myPromise 实例
 */
myPromise.all = function(promises){
  return new myPromise((resolve, reject)=>{
    // 记录已成功的 myPromise 数量
    let count = 0
    // 获取传入的 myPromise 数组长度(处理空数组情况,默认赋值 -1)
    let promisesLength = promises.length || -1
    // 存储所有 myPromise 的成功结果,保持与传入数组的顺序一致
    let arrResults = new Array(promisesLength)
    // 遍历所有 myPromise 实例
    for(let i=0; i<promisesLength; i++){
      // 注意:这里用 promises[i] 而不是 this,因为是静态方法
      promises[i].then(v=>{
        // 存储当前 myPromise 的成功结果,对应数组索引位置
        arrResults[i] = v
        // 成功数量加 1
        count++
        // 当成功数量等于数组长度时,说明所有 myPromise 都成功,返回结果数组
        if(count===promisesLength){
          resolve(arrResults)
        }
      },r=>{
        // 只要有一个 myPromise 失败,直接返回失败原因,终止后续处理
        reject(r)
      })
    }
  })
}

/**
 * myPromise 静态方法 race - 批量处理多个 myPromise 实例,返回第一个改变状态的 myPromise 结果(无论成功/失败)
 * @param {Array} promises - myPromise 实例数组
 * @returns {myPromise} 新的 myPromise 实例
 */
myPromise.race = function(promises){
  return new myPromise((resolve, reject)=>{
    // 遍历所有 myPromise 实例
    for(let i=0,iLen=promises.length; i<iLen; i++){
      // 监听每个 myPromise 的状态变化,只要有一个状态改变,立即透传其结果
      promises[i].then(v=>{
        resolve(v)
      },r=>{
        reject(r)
      })
    }
  })
}

/**
 * 自定义 myPromise 静态方法 resolveDelay - 延迟指定时间后创建一个成功态的 myPromise 实例
 * @param {*} value - 成功的结果值(可以是普通值或 myPromise 实例)
 * @param {Number} time - 延迟时间(毫秒)
 * @returns {myPromise} 新的 myPromise 实例
 */
myPromise.resolveDelay = function(value, time){
  return new myPromise((resolve, reject)=>{
    // 注意:原代码笔误 setTimeount → 修正为 setTimeout
    setTimeout(()=>{
      // 若传入的值是 myPromise 实例,监听其状态变化,透传结果
      if(value instanceof myPromise){
        value.then(resolve, reject)
      }else{
        // 若传入普通值,延迟后创建成功态 myPromise
        resolve(value)
      }
    }, time)
  })
}

/**
 * 自定义 myPromise 静态方法 rejectDelay - 延迟指定时间后创建一个失败态的 myPromise 实例
 * @param {*} reason - 失败的原因值
 * @param {Number} time - 延迟时间(毫秒)
 * @returns {myPromise} 新的 myPromise 实例
 */
myPromise.rejectDelay = function(reason, time){
  return new myPromise((resolve, reject)=>{
    // 注意:原代码笔误 setTimeount → 修正为 setTimeout
    setTimeout(()=>{
      // 延迟后创建失败态 myPromise
      reject(reason)
    },time)
  })
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值