手写ES6 Promise() 相关函数

手写 Promise() 相关函数:

Promise()、then()、catch()、finally()

js 复制代码
// 定义三种状态常量
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class MyPromise {
  /*
    定义状态和结果两个私有属性:
    1.使用 # 语法(ES2022+ 官方私有字段):在类中通过 # 前缀声明属性,该属性仅在类的内部可访问
    2.Symbol 作为属性键:用 Symbol 作为属性名,外部无法直接获取 Symbol 引用。
  */
  #state = PENDING // 状态
  #result = undefined // 结果
  #thenables = [] // 存储 then 方法回调的队列
  constructor(executor) {
    // resolve 和 reject 主要功能即为改变状态,设置结果
    const resolve = (value) => { // 解决时调用
      this.#changeState(FULFILLED, value)
    }
    const reject = (reason) => { // 拒绝时调用
      this.#changeState(REJECTED, reason)
    }
    try { // 只能捕获同步错误,无法捕获异步错误,如 setTimeout 里的错误
      executor(resolve, reject)
    } catch (err) {
      reject(err)
    }
  }

  // 改变状态和设置结果的私有方法
  #changeState(state, result) {
    if (this.#state !== PENDING) return
    this.#state = state
    this.#result = result
    this.#run()
  }

  // 处理 then 回调的私有方法
  #handleCallback(callback, resolve, reject) {
    if (typeof callback !== 'function') { // 状态穿透,即 then 方法返回的 Promise 状态与当前 Promise 状态保持一致
      // then 回调是微任务,需要放到微任务队列中执行
      queueMicrotask(() => { // 参考:https://developer.mozilla.org/zh-CN/docs/Web/API/Window/queueMicrotask
        const settled = this.#state === FULFILLED ? resolve : reject
        settled(this.#result)
      })
      return
    }
    queueMicrotask(() => {
      try {
        const result = callback(this.#result)
        // 如果返回值是一个 Promise,则等待它完成后再 resolve
        if (result instanceof MyPromise) {
          result.then(resolve, reject)
        } else {
          resolve(result)
        }
      } catch (err) {
        reject(err)
      }
    })
  }
  /*
    执行队列的私有方法,两个执行时机:
    1.Promise 状态改变时
    2.then 方法被调用时
  */
  #run() {
    if (this.#state === PENDING) return
    // 取出队列中的回调函数,依次执行(先进先出原则)
    while (this.#thenables.length) {
      const { onFulfilled, onRejected, resolve, reject } = this.#thenables.shift()
      try {
        if (this.#state === FULFILLED) { // 执行 onFulfilled 回调函数
          this.#handleCallback(onFulfilled, resolve, reject)
        } else { // 执行 onRejected 回调函数
          this.#handleCallback(onRejected, resolve, reject)
        }
      } catch (err) {
        reject(err)
      }
    }
  }
  /*
    onFulfilled 可选:一个在此 Promise 对象被兑现时异步执行的函数。它的返回值将成为 then() 返回的 Promise 对象的兑现值。
    onRejected 可选:一个在此 Promise 对象被拒绝时异步执行的函数。它的返回值将成为 catch() 返回的 Promise 对象的兑现值。
  */
  then(onFulfilled, onRejected) {
    return new MyPromise((resolve, reject) => {
      // 将四个回调函数放入队列,以便立即或将来处理
      this.#thenables.push({
        onFulfilled,
        onRejected,
        resolve,
        reject
      })
      // 启动队列处理
      this.#run()
    })
  }

  catch(onRejected) {
    return this.then(undefined, onRejected)
  }

  finally(onFinally) {
    this.then(
      (value) => {
        MyPromise.resolve(onFinally()).then(() => value)
      },
      (reason) => {
        MyPromise.resolve(onFinally()).then(() => { throw reason })
      }
    )
  }
}

验证测试 MyPromise() 函数

js 复制代码
const p1 = new Promise((resolve, reject) => {
  resolve(1)
  reject(2)
})
console.log('p1', p1)
const p2 = new Promise(() => { throw 123 })
console.log('p2', p2)
const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('setTimeout') // 无法捕获
  }, 0)
})
console.log('p3', p3)
const myP1 = new MyPromise((resolve, reject) => {
  resolve(1)
  reject(2)
})
console.log('myP1', myP1)
myP1.then(
  (res) => {
    console.log('res', res) // 1
    return res + 1
  },
  (err) => {
    console.log('err', err) // 1
  }
).then((res) => {
  console.log('res', res) // 2
  // throw 'error'
  return res + 1
}).then((res) => {
  console.log('res', res) // 3
}).catch((err) => {
  console.log('err', err)
}).finally(() => {
  console.log('finally')
})
const myP2 = new MyPromise(() => { throw 123 })
console.log('myP2', myP2)
const myP3 = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve('setTimeout') // 无法捕获
  }, 0)
})
console.log('myP3', myP3)

执行结果

相关推荐
烬头88219 分钟前
React Native鸿蒙跨平台实现二维码联系人APP(QRCodeContactApp)
javascript·react native·react.js·ecmascript·harmonyos
pas13612 分钟前
40-mini-vue 实现三种联合类型
前端·javascript·vue.js
摇滚侠21 分钟前
2 小时快速入门 ES6 基础视频教程
前端·ecmascript·es6
2601_9498333925 分钟前
flutter_for_openharmony口腔护理app实战+预约管理实现
android·javascript·flutter
军军君011 小时前
Three.js基础功能学习十三:太阳系实例上
前端·javascript·vue.js·学习·3d·前端框架·three
xiaoqi9222 小时前
React Native鸿蒙跨平台如何实现分类页面组件通过searchQuery状态变量管理搜索输入,实现了分类的实时过滤功能
javascript·react native·react.js·ecmascript·harmonyos
qq_177767373 小时前
React Native鸿蒙跨平台实现应用介绍页,实现了应用信息卡片展示、特色功能网格布局、权限/联系信息陈列、评分展示、模态框详情交互等通用场景
javascript·react native·react.js·ecmascript·交互·harmonyos
2603_949462103 小时前
Flutter for OpenHarmony社团管理App实战:预算管理实现
android·javascript·flutter
wuhen_n3 小时前
JavaScript内存管理与执行上下文
前端·javascript
Hi_kenyon3 小时前
理解vue中的ref
前端·javascript·vue.js