本文将从Promise核心知识入手,循序渐进讲解Promise的实现逻辑,包含完整可运行的手写代码,重点解析异步调用、链式调用的核心原理,同时纠正学习过程中容易出现的认知误区(结合自身学习疑惑),帮助彻底吃透Promise底层。

一、Promise核心前置知识(必懂)

1.1 Promise的核心作用

Promise 是JavaScript中用于处理异步操作的对象,核心解决「回调地狱」问题,将异步操作的「成功/失败」结果以统一的方式管理,支持链式调用,让异步代码更简洁、可维护。

1.2 Promise的三大核心状态(不可逆)

Promise有且只有三种状态,状态一旦变更,永久不可修改,这是Promise的核心特性之一:

  • pending(等待态):初始状态,异步操作未完成(如定时器未执行、接口未返回)。
  • fulfilled(成功态):异步操作成功完成,状态从pending → fulfilled,会触发then的成功回调。
  • rejected(失败态):异步操作失败,状态从pending → rejected,会触发then的失败回调或catch回调。

⚠️ 关键:状态只能从「pending」转向「fulfilled」或「rejected」,不能反向,也不能从fulfilled/rejected转向其他状态。

1.3 事件循环与微任务(Promise异步核心)

Promise的then、catch、finally回调均为「微任务」,优先级高于setTimeout等「宏任务」,执行顺序遵循:

同步代码 → 所有微任务 → 一个宏任务 → 所有微任务 → 一个宏任务...(循环)

这也是为什么原生Promise的then回调永远是异步执行,即便同步resolve,也不会立刻执行then回调。

1.4 Promise的核心逻辑梳理

Promise的本质是「状态管理+回调缓存+异步触发」,核心逻辑可总结为3点:

  1. 初始化时,状态为pending,缓存成功/失败回调队列。
  2. 异步操作完成后,调用resolve/reject,变更状态,并触发对应回调队列。
  3. then方法返回新的Promise,实现链式调用,通过手动触发下一个Promise的resolve/reject,传递结果。

二、手写Promise完整版代码(全量可运行)

以下代码包含Promise所有核心功能:状态管理、异步支持、链式调用、微任务、catch/finally、static方法(resolve/reject/all/race),结构严格按照要求组织:

class MyPromise {
  // 三大核心状态(静态常量,所有实例共享)
  static PENDING = 'pending'
  static FULFILLED = 'fulfilled'
  static REJECTED = 'rejected'

  // 构造函数:接收执行器(executor),立即执行
  constructor(executor) {
    // 实例状态、成功结果、失败原因
    this.status = MyPromise.PENDING
    this.value = undefined
    this.reason = undefined

    // 回调队列:缓存pending状态时的then回调(成功/失败)
    this.onFulfilledCallbacks = []
    this.onRejectedCallbacks = []

    // 成功回调:变更状态,触发成功队列
    const resolve = (value) => {
      // 状态不可逆:只有pending才能变更为fulfilled
      if (this.status !== MyPromise.PENDING) return
      this.status = MyPromise.FULFILLED
      this.value = value

      // 微任务:批量执行成功回调队列(对齐原生异步行为)
      queueMicrotask(() => {
        this.onFulfilledCallbacks.forEach(fn => fn())
        this.onFulfilledCallbacks = [] // 执行后清空队列,避免重复执行
      })
    }

    // 失败回调:变更状态,触发失败队列
    const reject = (reason) => {
      // 状态不可逆:只有pending才能变更为rejected
      if (this.status !== MyPromise.PENDING) return
      this.status = MyPromise.REJECTED
      this.reason = reason

      // 微任务:批量执行失败回调队列
      queueMicrotask(() => {
        this.onRejectedCallbacks.forEach(fn => fn())
        this.onRejectedCallbacks = []
      })
    }

    // 执行器异常处理:执行器抛出错误,直接触发reject
    try {
      executor(resolve, reject)
    } catch (err) {
      reject(err)
    }
  }

  // then方法:核心链式调用方法,返回新Promise
  then(onFulfilled, onRejected) {
    // 缺省兜底:实现值穿透、错误穿透(不传回调时,默认传递结果/错误)
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val
    onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err }

    // 链式核心:每一个then都返回新的Promise,确保可以连续调用
    return new MyPromise((nextResolve, nextReject) => {
      // 封装成功回调处理逻辑(复用代码)
      const handleFulfilled = () => {
        try {
          // 执行当前then的成功回调,获取返回值
          const res = onFulfilled(this.value)
          // 如果返回值是Promise,等待其完成后,再触发下一个Promise
          if (res instanceof MyPromise) {
            res.then(nextResolve, nextReject)
          } else {
            // 普通值,直接触发下一个Promise的成功
            nextResolve(res)
          }
        } catch (err) {
          // 回调执行出错,触发下一个Promise的失败
          nextReject(err)
        }
      }

      // 封装失败回调处理逻辑(复用代码)
      const handleRejected = () => {
        try {
          // 执行当前then的失败回调,获取返回值
          const res = onRejected(this.reason)
          // 如果返回值是Promise,等待其完成后,再触发下一个Promise
          if (res instanceof MyPromise) {
            res.then(nextResolve, nextReject)
          } else {
            // 普通值,直接触发下一个Promise的成功(错误穿透后,后续then可正常接收)
            nextResolve(res)
          }
        } catch (err) {
          nextReject(err)
        }
      }

      // 根据当前Promise的状态,执行对应逻辑
      if (this.status === MyPromise.FULFILLED) {
        // 已成功:微任务执行成功回调(对齐原生异步)
        queueMicrotask(handleFulfilled)
      } else if (this.status === MyPromise.REJECTED) {
        // 已失败:微任务执行失败回调
        queueMicrotask(handleRejected)
      } else {
        // pending状态:将回调存入队列,等待resolve/reject触发
        this.onFulfilledCallbacks.push(handleFulfilled)
        this.onRejectedCallbacks.push(handleRejected)
      }
    })
  }

  // catch方法:错误捕获,本质是then的语法糖(不传成功回调,只传失败回调)
  catch(onRejected) {
    return this.then(null, onRejected)
  }

  // finally方法:无论成功/失败,都会执行,不改变链式传递的值
  finally(cb) {
    return this.then(
      val => MyPromise.resolve(cb()).then(() => val), // 成功时,执行cb后传递原值
      err => MyPromise.resolve(cb()).then(() => { throw err }) // 失败时,执行cb后抛出原错误
    )
  }

  // 静态resolve方法:快速创建一个已成功的Promise
  static resolve(value) {
    return new MyPromise(resolve => resolve(value))
  }

  // 静态reject方法:快速创建一个已失败的Promise
  static reject(reason) {
    return new MyPromise((_, reject) => reject(reason))
  }

  // 静态all方法:接收数组,所有Promise成功才返回,一个失败则整体失败
  static all(promises) {
    return new MyPromise((resolve, reject) => {
      // 校验参数:必须是数组
      if (!Array.isArray(promises)) {
        return reject(new TypeError('Promise.all() 接收的参数必须是数组'))
      }

      const result = [] // 存储所有成功结果(顺序与传入一致)
      let resolvedCount = 0 // 计数:已成功的Promise数量

      // 空数组直接resolve(原生行为)
      if (promises.length === 0) {
        return resolve(result)
      }

      // 遍历所有Promise,统一处理(普通值会被MyPromise.resolve包装)
      promises.forEach((promise, index) => {
        MyPromise.resolve(promise).then(res => {
          result[index] = res // 按传入顺序存储结果
          resolvedCount++ // 成功计数+1

          // 所有Promise都成功,才resolve结果数组
          if (resolvedCount === promises.length) {
            resolve(result)
          }
        }).catch(err => {
          // 只要有一个失败,立即reject(整体失败)
          reject(err)
        })
      })
    })
  }

  // 静态race方法:接收数组,谁先完成(成功/失败),就返回谁的结果
  static race(promises) {
    return new MyPromise((resolve, reject) => {
      // 校验参数:必须是数组
      if (!Array.isArray(promises)) {
        return reject(new TypeError('Promise.race() 接收的参数必须是数组'))
      }

      // 空数组:永远pending(原生行为)
      if (promises.length === 0) {
        return
      }

      // 遍历所有Promise,谁先完成,就触发对应的resolve/reject
      promises.forEach(promise => {
        MyPromise.resolve(promise).then(resolve).catch(reject)
      })
    })
  }
}

三、循序渐进实现Promise(从基础到完整)

3.1 第一步:实现基础状态管理(同步场景)

最基础的Promise,只实现状态管理和同步resolve/reject,不支持异步和链式调用:

class MyPromise {
  static PENDING = 'pending'
  static FULFILLED = 'fulfilled'
  static REJECTED = 'rejected'

  constructor(executor) {
    this.status = MyPromise.PENDING
    this.value = undefined
    this.reason = undefined

    const resolve = (value) => {
      if (this.status !== MyPromise.PENDING) return
      this.status = MyPromise.FULFILLED
      this.value = value
    }

    const reject = (reason) => {
      if (this.status !== MyPromise.PENDING) return
      this.status = MyPromise.REJECTED
      this.reason = reason
    }

    try {
      executor(resolve, reject)
    } catch (err) {
      reject(err)
    }
  }

  // 基础then方法:只处理同步状态
  then(onFulfilled, onRejected) {
    if (this.status === MyPromise.FULFILLED) {
      onFulfilled(this.value)
    }
    if (this.status === MyPromise.REJECTED) {
      onRejected(this.reason)
    }
    // 此时未处理pending状态(异步场景会失效)
  }
}

⚠️ 问题:异步场景(如setTimeout内resolve)会失效,因为then执行时,状态还是pending,没有缓存回调,回调会丢失。

3.2 第二步:加入回调队列(支持异步场景)

核心解决「异步场景回调丢失」问题:pending状态时,将then回调存入队列,等resolve/reject触发后,批量执行队列:

class MyPromise {
  static PENDING = 'pending'
  static FULFILLED = 'fulfilled'
  static REJECTED = 'rejected'

  constructor(executor) {
    this.status = MyPromise.PENDING
    this.value = undefined
    this.reason = undefined

    // 新增:回调队列,缓存pending状态的then回调
    this.onFulfilledCallbacks = []
    this.onRejectedCallbacks = []

    const resolve = (value) => {
      if (this.status !== MyPromise.PENDING) return
      this.status = MyPromise.FULFILLED
      this.value = value
      // 新增:状态变更后,批量执行成功回调队列
      this.onFulfilledCallbacks.forEach(fn => fn())
    }

    const reject = (reason) => {
      if (this.status !== MyPromise.PENDING) return
      this.status = MyPromise.REJECTED
      this.reason = reason
      // 新增:批量执行失败回调队列
      this.onRejectedCallbacks.forEach(fn => fn())
    }

    try {
      executor(resolve, reject)
    } catch (err) {
      reject(err)
    }
  }

  then(onFulfilled, onRejected) {
    if (this.status === MyPromise.FULFILLED) {
      onFulfilled(this.value)
    }
    if (this.status === MyPromise.REJECTED) {
      onRejected(this.reason)
    }
    // 新增:pending状态,将回调存入队列(关键!)
    if (this.status === MyPromise.PENDING) {
      this.onFulfilledCallbacks.push(() => {
        onFulfilled(this.value)
      })
      this.onRejectedCallbacks.push(() => {
        onRejected(this.reason)
      })
    }
  }
}

✅ 关键理解:此时存入队列的是「匿名函数」,只是「保存函数」,不执行;只有当resolve/reject触发,才会遍历队列执行函数(异步场景生效)。

比如:setTimeout内resolve,then执行时状态是pending,回调被存入队列,1秒后resolve触发,才执行队列中的回调。

3.3 第三步:实现链式调用(核心难点)

链式调用的核心的是「then返回新的Promise」+「手动触发下一个Promise的resolve/reject」,这也是我之前容易误解的点,重点解析:

3.3.1 核心误区纠正(我之前的错误认知)

❌ 错误认知:所有then的this.status都是第一个Promise的状态,链式调用靠第一个Promise的状态驱动。

❌ 错误认知:回调队列是链式调用的核心,没有队列就不能链式。

✅ 正确认知:

  1. 每一个then都返回「全新的Promise实例」,新实例的初始状态都是pending(和第一个Promise无关)。
  2. 链式调用的核心不是队列,而是「nextResolve/resnextReject」(then内部新Promise的resolve/reject)。
  3. 队列的作用是「缓存异步回调」,和链式能不能通无关;链式能通的唯一原因是「上一个then手动触发下一个Promise的resolve/reject」。

3.3.2 链式调用核心代码实现

then(onFulfilled, onRejected) {
  // 缺省兜底(可选,优化体验)
  onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val
  onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err }

  // 链式核心1:返回新的Promise
  return new MyPromise((nextResolve, nextReject) => {
    const handleFulfilled = () => {
      try {
        const res = onFulfilled(this.value)
        // 链式核心2:手动触发下一个Promise的resolve(传递结果)
        nextResolve(res)
      } catch (err) {
        nextReject(err)
      }
    }

    const handleRejected = () => {
      try {
        const res = onRejected(this.reason)
        nextResolve(res)
      } catch (err) {
        nextReject(err)
      }
    }

    if (this.status === MyPromise.FULFILLED) {
      handleFulfilled()
    } else if (this.status === MyPromise.REJECTED) {
      handleRejected()
    } else {
      // pending状态:缓存的是封装后的handleFulfilled/handleRejected
      this.onFulfilledCallbacks.push(handleFulfilled)
      this.onRejectedCallbacks.push(handleRejected)
    }
  })
}

3.3.3 链式调用执行流程(彻底看懂)

以异步场景为例,拆解链式调用的完整流程:

// 1. 创建第一个Promise(p1),异步resolve
const p1 = new MyPromise((resolve) => {
  setTimeout(() => resolve(100), 1000)
})

// 2. p1.then() → 返回新Promise(p2)
const p2 = p1.then(res => {
  console.log('第一层then:', res) // 100
  return res + 10 // 返回普通值,传递给p2
})

// 3. p2.then() → 返回新Promise(p3)
p2.then(res => {
  console.log('第二层then:', res) // 110
})
  1. p1初始化:状态pending,executor中setTimeout异步挂起,then执行时状态pending,将handleFulfilled存入p1的队列。
  2. p2初始化:状态pending(新实例,和p1无关),等待被触发。
  3. 1秒后,p1.resolve(100):状态变为fulfilled,遍历队列执行handleFulfilled。
  4. 执行p1.then的回调,拿到返回值110,手动调用nextResolve(110) → p2的状态变为fulfilled。
  5. p2.then执行:检测到p2已fulfilled,执行回调,打印110,完成链式传递。

✅ 关键:p2的状态不是天生fulfilled,是p1的then回调中,通过nextResolve手动触发的;没有nextResolve,链式会彻底断掉(这是我之前debug发现的核心真相)。

3.4 第四步:加入微任务(对齐原生异步行为)

原生Promise的then回调永远是微任务,即便同步resolve,也不会立刻执行,而是等同步代码执行完再执行。我们通过queueMicrotask实现这一特性:

// resolve中加入微任务
const resolve = (value) => {
  if (this.status !== MyPromise.PENDING) return
  this.status = MyPromise.FULFILLED
  this.value = value
  // 新增:微任务执行回调队列
  queueMicrotask(() => {
    this.onFulfilledCallbacks.forEach(fn => fn())
    this.onFulfilledCallbacks = []
  })
}

// then中,已fulfilled/rejected状态也加入微任务
if (this.status === MyPromise.FULFILLED) {
  queueMicrotask(handleFulfilled)
} else if (this.status === MyPromise.REJECTED) {
  queueMicrotask(handleRejected)
}

测试微任务优先级:

console.log('同步开始')
MyPromise.resolve().then(() => console.log('Promise微任务'))
setTimeout(() => console.log('setTimeout宏任务'), 0)
console.log('同步结束')
// 输出顺序:同步开始 → 同步结束 → Promise微任务 → setTimeout宏任务(和原生一致)

3.5 第五步:补充catch/finally和静态方法

这部分都是基于then的语法糖,难度不大,核心逻辑:

  • catch:本质是then(null, onRejected),专门捕获失败回调。
  • finally:无论成功/失败都执行,不改变链式传递的值,通过MyPromise.resolve(cb())确保cb异步执行。
  • static resolve/reject:快速创建已成功/失败的Promise,简化代码。
  • static all/race:处理多个Promise的批量操作(all等待全部成功,race取最快结果)。

四、核心总结(重中之重)

4.1 Promise核心逻辑

  1. 状态不可逆:pending → fulfilled/rejected,一旦变更,永久不变。
  2. 回调缓存:pending状态时,then回调存入队列,等待resolve/reject触发。
  3. 链式调用:then返回新Promise,通过nextResolve/nextReject传递结果(核心中的核心)。
  4. 异步特性:所有回调均为微任务,优先级高于宏任务。

4.2 最容易踩的误区(我亲身经历)

  1. 误区1:then的this指向第一个Promise → 纠正:谁调用then,this就指向谁(p1.then的this是p1,p2.then的this是p2)。
  2. 误区2:链式调用靠队列 → 纠正:队列只负责缓存异步回调,链式能通的核心是nextResolve/nextReject。
  3. 误区3:新Promise实例初始状态是fulfilled → 纠正:所有new MyPromise的实例,初始状态都是pending,需手动触发resolve/reject。
  4. 误区4:队列中的函数会立即执行 → 纠正:队列中存的是函数引用,只有resolve/reject触发,才会遍历执行。

4.3 最终结论

手写Promise的核心,不在于代码有多复杂,而在于理解「状态管理」「回调缓存」「链式接力」「微任务异步」这四个关键点。其中,链式调用的灵魂是「nextResolve/nextReject」,队列是异步场景的补充,二者结合,才能实现和原生一致的Promise功能。

五、测试用例(验证代码可用性)

// 1. 异步链式 + 错误穿透 + finally
new MyPromise((resolve) => {
  setTimeout(() => resolve(100), 1000)
})
.then(res => {
  console.log('第一层:', res) // 100
  return res + 200
})
.then(res => {
  console.log('第二层:', res) // 300
  throw new Error('手动报错')
})
.catch(err => {
  console.log('捕获错误:', err.message) // 手动报错
})
.finally(() => {
  console.log('finally必定执行')
})

// 2. Promise.all测试(全部成功)
const p1 = MyPromise.resolve(1)
const p2 = new MyPromise(resolve => setTimeout(() => resolve(2), 500))
const p3 = 3 // 普通值会被包装
MyPromise.all([p1, p2, p3]).then(res => {
  console.log('all成功:', res) // [1, 2, 3]
})

// 3. Promise.race测试(取最快结果)
const race1 = new MyPromise(resolve => setTimeout(() => resolve('慢'), 1000))
const race2 = new MyPromise(resolve => setTimeout(() => resolve('快'), 100))
MyPromise.race([race1, race2]).then(res => {
  console.log('race胜利:', res) // 快
})

// 4. 微任务优先级测试
console.log('同步开始')
MyPromise.resolve().then(() => console.log('Promise微任务'))
setTimeout(() => console.log('setTimeout宏任务'), 0)
console.log('同步结束')
(注:文档部分内容可能由 AI 生成)

标签: none

添加新评论