手写 Promise 核心逻辑保姆级教程

一、什么是 Promise?

Promise 是 JavaScript 中处理异步操作的解决方案。想象你叫外卖:

  1. 你下单(创建 Promise)

  2. 等待餐品制作(pending 状态)

  3. 收到外卖(fulfilled 状态)或配送失败(rejected 状态)

Promise 的三种状态:

状态说明转换规则
pending等待中(初始状态)可转换为其他状态
fulfilled已成功不可逆
rejected已失败不可逆

二、手写 Promise 分步实现

1. 基础框架搭建

class MyPromise {
  constructor(executor) {
    this.state = 'pending' // 初始状态
    this.value = null     // 成功值
    this.reason = null    // 失败原因
    this.onFulfilledCallbacks = [] // 成功回调队列
    this.onRejectedCallbacks = []  // 失败回调队列

    // 成功回调
    const resolve = (value) => {
      if (this.state === 'pending') {
        this.state = 'fulfilled'
        this.value = value
        this.onFulfilledCallbacks.forEach(fn => fn())
      }
    }

    // 失败回调
    const reject = (reason) => {
      if (this.state === 'pending') {
        this.state = 'rejected'
        this.reason = reason
        this.onRejectedCallbacks.forEach(fn => fn())
      }
    }

    try {
      executor(resolve, reject) // 立即执行传入的函数
    } catch (err) {
      reject(err) // 捕获同步错误
    }
  }
}

重点解析:

  • executor 是立即执行的函数,接收 resolve 和 reject 两个参数

  • 使用队列保存回调函数,确保异步执行顺序

  • try...catch 捕获执行器中的同步错误


2. 实现 then 方法

then(onFulfilled, onRejected) {
  // 处理值穿透(当参数不是函数时)
  onFulfilled = typeof onFulfilled === 'function' 
    ? onFulfilled 
    : value => value
    
  onRejected = typeof onRejected === 'function' 
    ? onRejected 
    : reason => { throw reason }

  // 创建新 Promise 用于链式调用
  const promise2 = new MyPromise((resolve, reject) => {
    // 统一处理函数
    const handle = (callback, value) => {
      setTimeout(() => { // 模拟微任务
        try {
          const x = callback(value)
          resolvePromise(promise2, x, resolve, reject)
        } catch (err) {
          reject(err)
        }
      })
    }

    if (this.state === 'fulfilled') {
      handle(onFulfilled, this.value)
    } else if (this.state === 'rejected') {
      handle(onRejected, this.reason)
    } else { // pending 状态
      this.onFulfilledCallbacks.push(() => {
        handle(onFulfilled, this.value)
      })
      this.onRejectedCallbacks.push(() => {
        handle(onRejected, this.reason)
      })
    }
  })

  return promise2
}

关键点解释:

  • 值穿透:当 then 的参数不是函数时,会创建默认函数传递值/抛出错误

  • 异步执行:使用 setTimeout 模拟微任务队列

  • 链式调用:每个 then 都返回新 Promise 对象


3. 实现 resolvePromise

function resolvePromise(promise2, x, resolve, reject) {
  // 循环引用检查
  if (promise2 === x) {
    return reject(new TypeError('循环引用'))
  }

  let called = false // 防止多次调用

  if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
    try {
      const then = x.then
      if (typeof then === 'function') { // 判断是否为 Promise
        then.call(
          x,
          y => { // 成功回调
            if (called) return
            called = true
            resolvePromise(promise2, y, resolve, reject)
          },
          r => { // 失败回调
            if (called) return
            called = true
            reject(r)
          }
        )
      } else { // 普通对象
        resolve(x)
      }
    } catch (err) { // 捕获访问 then 属性的错误
      if (called) return
      called = true
      reject(err)
    }
  } else { // 基本类型值
    resolve(x)
  }
}

为什么需要这个函数?

  • 处理不同返回值类型(普通值/Promise 对象)

  • 递归解析嵌套 Promise

  • 防止循环引用导致死循环


三、使用案例详解

案例 1:基础链式调用

const p = new MyPromise((resolve) => {
  setTimeout(() => resolve(100), 1000)
})

p.then(res => {
  console.log('第一次处理:', res) // 1秒后输出 100
  return res * 2
}).then(res => {
  console.log('第二次处理:', res) // 输出 200
})

执行流程:

  1. 创建 Promise 对象,1秒后 resolve

  2. 第一个 then 接收 100,返回 200

  3. 第二个 then 接收 200 进行输出


案例 2:错误处理

new MyPromise((resolve, reject) => {
  setTimeout(() => reject(new Error('网络错误')), 500)
})
.then(null, err => {
  console.log('捕获错误:', err.message) // 输出 "网络错误"
  return '默认值'
})
.then(res => {
  console.log('后续处理:', res) // 输出 "默认值"
})

流程说明:

  1. Promise 在 0.5 秒后 reject

  2. 第一个 then 的第二个参数捕获错误

  3. 返回新值传递给后续 then


案例 3:静态方法

// 并行执行多个 Promise
MyPromise.all([
  new MyPromise(r => setTimeout(() => r(1), 300)),
  new MyPromise(r => setTimeout(() => r(2), 200)),
  3 // 普通值会自动转换为 resolved Promise
]).then(res => {
  console.log('all 结果:', res) // 约300ms后输出 [1, 2, 3]
})

// 竞速执行
MyPromise.race([
  new MyPromise(r => setTimeout(() => r('快'), 100)),
  new MyPromise(r => setTimeout(() => r('慢'), 200))
]).then(res => {
  console.log('race 结果:', res) // 约100ms后输出 "快"
})

方法对比:

方法特点适用场景
all等待全部完成需要收集多个异步结果
race采用第一个完成的结果超时控制、竞速请求
resolve快速创建成功 Promise包装已知值
reject快速创建失败 Promise抛出已知错误

四、常见问题解答

Q1: 为什么要用 setTimeout?

  • 原生 Promise 使用微任务队列(microtask)

  • 这里用宏任务模拟实现异步效果

  • 实际开发中应使用 queueMicrotask

Q2: 如何实现真正的微任务?

// 替换 handle 函数中的 setTimeout
if (typeof queueMicrotask === 'function') {
  queueMicrotask(() => { /* 逻辑 */ })
} else {
  Promise.resolve().then(() => { /* 逻辑 */ })
}

Q3: 为什么要有 called 标志?

防止以下情况:

const thenable = {
  then: (resolve, reject) => {
    resolve(1)
    reject(2) // 错误调用
  }
}

called 标志确保只调用一次 resolve/reject


五、手写实现总结

实现要点说明
状态管理使用 state 属性跟踪状态
回调队列收集 pending 状态的回调
异步执行使用 setTimeout 模拟
链式调用每个 then 返回新 Promise
错误冒泡自动传递错误到最近的 catch
值穿透处理非函数参数

六、与原生 Promise 的差异

特性手写实现原生 Promise
微任务队列用宏任务模拟真正的微任务
性能优化未优化高度优化
错误堆栈简单追踪完整错误追踪
特殊对象处理基础实现完整规范支持

七、最佳实践建议

  1. 生产环境:使用原生 Promise

  2. 学习目的:通过手写理解原理

  3. 扩展功能

    • 添加取消功能

    • 实现进度通知

    • 增加超时控制

通过这个手写实现,你已掌握:

  • Promise 的核心运行机制

  • 异步任务队列管理

  • 链式调用的实现原理

  • 错误处理的最佳实践

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值