手写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)

执行结果

相关推荐
Nejosi_念旧2 小时前
TypeScript 泛型讲解
前端·javascript·typescript
layman05282 小时前
vue——v-pre的使用
前端·javascript·vue.js
zizisuo2 小时前
Java集合框架深度剖析:结构、并发与设计模式全解析
java·javascript·数据结构·设计模式
要加油哦~2 小时前
刷题 | 牛客 - js中等题-下(更ing)30/54知识点&解答
java·开发语言·javascript
Python涛哥2 小时前
前端流行框架Vue3教程:25. 组件保持存活
前端·javascript·vue.js
Raink老师3 小时前
1.3 如何安装 TypeScript?
前端·javascript·typescript
ST_小罗3 小时前
【Web前端】JavaScript入门与基础(二)
开发语言·前端·javascript
Greatlifeee3 小时前
VUE3+TS实现图片缩放移动弹窗
前端·javascript·vue.js
笔直向前冲4 小时前
el-table控制type=“expand“展开列 根据条件显示或隐藏展开按钮
javascript·vue.js·elementui