手写ES6 Promise() 相关函数

news2025/5/25 9:57:40

手写 Promise() 相关函数:

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

// 定义三种状态常量
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() 函数

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)

执行结果

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2385270.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【NLP 76、Faiss 向量数据库】

压抑与痛苦,那些辗转反侧的夜,终会让我们更加强大 —— 25.5.20 Faiss(Facebook AI Similarity Search)是由 Facebook AI 团队开发的一个开源库,用于高效相似性搜索的库,特别适用于大规模向…

软件名称:系统日志监听工具 v1.0

软件功能:一款基于 PyQt5 开发的 Windows 系统日志监听工具,适用于系统运维、网络管理、故障排查等场景,具备以下核心功能: 支持监听系统三大日志源:应用程序 / 系统 / 安全日志实时抓取新日志事件,自动滚…

Spring AI 之结构化输出转换器

截至 2024 年 2 月 5 日,旧的 OutputParser、BeanOutputParser、ListOutputParser 和 MapOutputParser 类已被弃用,取而代之的是新的 StructuredOutputConverter、BeanOutputConverter、ListOutputConverter 和 MapOutputConverter 实现类。后者可直接替换前者,并提供相同的…

Java虚拟机面试题:内存管理(上)

🧑 博主简介:CSDN博客专家,历代文学网(PC端可以访问:https://literature.sinhy.com/#/?__c1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,精通Java编…

进程间通信I·匿名管道

目录 进程间通信(IPC) 含义 目的 分类 匿名管道 原理 创建过程 特性 四大情况 close问题 代码练习 简单通信 进程池 小知识 进程间通信(IPC) 含义 就是让不同的进程能看到同一份资源,实现数据交流。 …

Ubuntu Linux系统的基本命令详情

1.Ubuntu Linux是以桌面应用为主的Linux发行版操作系统 2.Ubuntu的用户使用 在登录系统一般使用在安装系统时建立的普通用户登录,如果要使用超级用户权限 #sudo ---执行命令 sudo passwd ---修改用户密码 su - root ---切换超级用户 系统的不同,命令也不…

大数据治理:理论、实践与未来展望(二)

书接上文 文章目录 七、大数据治理的未来发展趋势(一)智能化与自动化(二)数据隐私与安全的强化(三)数据治理的云化(四)数据治理的跨行业合作(五)数据治理的生…

PCB布局设计

PCB布局设计 一、原理图到PCB转换前的准备工作 在将原理图转换为PCB之前,我们需要进行一系列准备工作,确保设计的正确性和完整性。这一步骤至关重要,可以避免后续PCB设计中出现不必要的错误。 // 原理图转PCB前必要检查步骤 // 1. 仔细检查…

esp32+IDF V5.1.1版本编译freertos报错

error: portTICK_RATE_MS undeclared (first use in this function); did you mean portTICK_PERIOD_MS 解决方法: 使用命令 idf.py menuconfig 打开配置界面配置freeRtos 使能configENABLE_BACKWARD_COMPATIBLITY

笔记本6GB本地可跑的图生视频项目(FramePack)

文章目录 (一)简介(二)本地执行(2.1)下载(2.2)更新(2.3)运行(2.4)生成 (三)注意(3.1)效…

SpringMVC实战:动态时钟

引言 在现代 Web 开发中,选择一个合适的框架对于项目的成功至关重要。Spring MVC 作为 Spring 框架的核心模块之一,以其清晰的架构、强大的功能和高度的可配置性,成为了 Java Web 开发领域的主流选择。本文将通过一个“动态时钟”的实战项目…

哈希表的实现(上)

前言 在C98中,STL提供了底层为红黑树结构的一系列关联式容器,在查询时效率可达到,即最差情况下需要比较红黑树的高度次,当树中的节点非常多时,查询效率也不理想。最好的查询是,进行很少的比较次数就能够将…

【Java高阶面经:微服务篇】1.微服务架构核心:服务注册与发现之AP vs CP选型全攻略

一、CAP理论在服务注册与发现中的落地实践 1.1 CAP三要素的技术权衡 要素AP模型实现CP模型实现一致性最终一致性(Eureka通过异步复制实现)强一致性(ZooKeeper通过ZAB协议保证)可用性服务节点可独立响应(支持分区存活)分区期间无法保证写操作(需多数节点可用)分区容错性…

实验7 HTTP协议分析与测量

实验7 HTTP协议分析与测量 1、实验目的 了解HTTP协议及其报文结构 了解HTTP操作过程:TCP三次握手、请求和响应交互 掌握基于tcpdump和wireshark软件进行HTTP数据包抓取和分析技术 2、实验环境 硬件要求:阿里云云主机ECS 一台。 软件要求&#xff1…

python:机器学习概述

本文目录: 一、人工智能三大概念二、学习方式三、人工智能发展史**1950-1970****1980-2000****2010-2017****2017-至今** 四、机器学习三要素五、常见术语六、数据集的划分七、常见算法分类八、机器学习的建模流程九、特征工程特征工程包括**五大步**:特…

得力DE-620K针式打印机打印速度不能调节维修一例

基本参数: 产品类型 票据针式打印机(平推式) 打印方式 串行点阵击打式 打印宽度 85列 打印针数 24针 可靠性 4亿次/针 色带性能 1000万字符纠错 复写能力 7份(1份原件+6份拷贝) 缓冲区 128KB 接口类型 …

java基础(继承)

什么是继承 继承好处 提高代码的复用性 继承注意事项 权限修饰符 单继承、Object类 冲突: 方法重写 扩展: 其实我们不想看地址,地址看来没用,我们是用来看对象有没有问题 重写toString: 比如这个如果返回的是地址值,…

基于cornerstone3D的dicom影像浏览器 第二十二章 mpr + vr

系列文章目录 第一章 下载源码 运行cornerstone3D example 第二章 修改示例crosshairs的图像源 第三章 vitevue3cornerstonejs项目创建 第四章 加载本地文件夹中的dicom文件并归档 第五章 dicom文件生成png,显示检查栏,序列栏 第六章 stack viewport 显…

MySQL:游标 cursor 句柄

当我们select * from emp 可以查看所有的数据 这个数据就相当于一个数据表 游标的作用相当于一个索引 一个指针 指向每一个数据 假设说我要取出员工中薪资最高的前五名成员 就要用到limit关键字 但是这样太麻烦了 所以这里用到了游标 游标的声明: declare my…

二、ZooKeeper 集群部署搭建

作者:IvanCodes 日期:2025年5月24日 专栏:Zookeeper教程 我们这次教程将以 hadoop01 (192.168.121.131), hadoop02 (192.168.121.132), hadoop03 (192.168.121.133) 三台Linux服务器为例,搭建一个ZooKeeper 3.8.4集群。 一、下载…