手写Promise完整介绍
Promise是一种用于处理异步操作的机制,它可以将异步操作的结果以同步的方式进行处理和返回。在JavaScript中,Promise是一种内置对象,但我们也可以手动实现一个Promise类来更好地理解其原理和工作方式。
Promise的特性
首先,让我们来介绍一下:
- Promise有三种状态:
pending(进行中)、fulfilled(已成功)和rejected(已失败)。初始状态为pending,当异步操作完成时,可以变为fulfilled或rejected。 - Promise具有链式调用的特性。通过then方法可以注册回调函数,在异步操作完成后执行这些回调函数。
then方法返回一个新的Promise对象,使得多个异步操作可以按顺序执行。 - Promise可以通过
resolve方法将状态从pending变为fulfilled,并传递一个值作为成功的结果;也可以通过reject方法将状态从pending变为rejected,并传递一个原因作为失败的结果。 - Promise可以通过
catch方法捕获错误,并处理错误情况。 - Promise还提供了一些静态方法,如
resolve、reject、all和race等。其中,resolve方法返回一个已经成功的Promise对象;reject方法返回一个已经失败的Promise对象;all方法接收一个包含多个Promise对象的数组,并在所有Promise对象都成功时返回一个包含所有结果的新Promise对象;race方法接收一个包含多个Promise对象的数组,并在任意一个Promise对象成功或失败时返回相应结果。
接下来,我们从源码角度讲解一下手写的Promise类。
手写Promise
定义常量
首先,我们定义了三个常量:PENDING表示promise的初始状态,FULFILLED表示promise成功的状态,REJECTED表示promise失败的状态。
js
复制代码
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
定义MyPromise类
接下来,我们定义了一个MyPromise类,该类拥有以下属性:
- state:表示promise的当前状态,默认为PENDING。
- value:表示promise成功时的返回值,默认为undefined。
- reason:表示promise失败时的错误原因,默认为undefined。
- onFulfilledCallbacks:用于存储成功回调函数的数组。
- onRejectedCallbacks:用于存储失败回调函数的数组。
构造函数(constructor)中接受一个executor函数作为参数,该函数接受两个参数:resolve和reject。我们在构造函数中定义了resolve和reject函数,并将它们传递给executor函数。如果executor函数执行成功,则调用resolve函数,如果执行失败,则调用reject函数。
js
复制代码
class MyPromise {constructor(executor) {this.state = PENDINGthis.value = undefinedthis.reason = undefinedthis.onFulfilledCallbacks = []this.onRejectedCallbacks = []const resolve = (value) => {if (this.state = PENDING) {this.state = FULFILLEDthis.value = valuethis.onFulfilledCallbacks.forEach(fn => fn())}}const reject = (reason) => {if (this.state === PENDING) {this.state = REJECTEDthis.reason = reasonthis.onRejectedCallbacks.forEach(fn => fn())}}try {executor(resolve, reject)} catch (err) {reject(err)}}
}
- 在
resolve函数中,我们首先判断promise的当前状态是否为PENDING。如果是,那么将状态改为FULFILLED并将返回值赋给value属性,并依次调用成功回调数组中的回调函数。 - 在
reject函数中,同样首先判断promise的当前状态是否为PENDING。如果是,那么将状态改为REJECTED并将错误原因赋给reason属性,并依次调用失败回调数组中的回调函数。 - 在构造函数的末尾,通过
try-catch语句执行executor函数。如果执行过程中有错误抛出,那么调用reject函数将错误原因赋给reason属性。
resolve和reject
接下来是resolve和reject方法的实现。resolve方法将状态从pending变为fulfilled,并传递一个值作为成功的结果;reject方法将状态从pending变为rejected,并传递一个原因作为失败的结果。
js
复制代码static resolve(value) {return new MyPromise((resolve, reject) => {resolve(value)})}static reject(reason) {return new MyPromise((resolve, reject) => {reject(reason)})}
then
-
在then方法中,我们创建了一个新的MyPromise实例promise2,并在其构造函数中定义了resolve和reject函数。
-
根据当前promise的状态,分别处理不同的情况:
- 如果当前状态是PENDING,那么将onFulfilled和onRejected回调函数分别推入onFulfilledCallbacks和onRejectedCallbacks数组中。
- 如果当前状态是FULFILLED,那么异步地执行onFulfilled回调,并根据返回值调用resolve或reject函数。
- 如果当前状态是REJECTED,那么异步地执行onRejected回调,并根据返回值调用resolve或reject函数。
-
最后,返回promise2实例。
js
复制代码
then(onFulfilled, onRejected) {if (typeof onFulfilled !== 'function') {onFulfilled = function (value) {return value}}if (typeof onRejected !== 'function') {onRejected = function (reason) {throw reason}}let promise2 = new MyPromise((resolve, reject) => {switch (this.state) {case PENDING:this.onFulfilledCallbacks.push(() => {setTimeout(() => {try {const value = onFulfilled(this.value)resolve(value)} catch (err) {reject(err)}}, 0)})this.onRejectedCallbacks.push(() => {setTimeout(() => {try {const value = onRejected(this.reason)resolve(value)} catch (err) {reject(err)}}, 0)})breakcase FULFILLED:setTimeout(() => {try {const value = onFulfilled(this.value)resolve(value)} catch (err) {reject(err)}}, 0)breakcase REJECTED:setTimeout(() => {try {const value = onRejected(this.reason)resolve(value)} catch (err) {reject(err)}}, 0)break}})return promise2}
catch和finally
- catch方法是then方法的一个语法糖,用于捕获promise链中的错误。它接受一个参数onRejected,表示失败的回调函数,然后调用then方法,并将onRejected作为第二个参数传入。
- finally方法是then方法的另一个语法糖,无论promise的状态是成功还是失败,都会执行finally方法中的回调函数。它接受一个参数fn,并在then方法中通过两个回调函数分别调用fn,并根据返回值执行resolve或reject函数
js
复制代码catch(onRejected) {return this.then(null, onRejected)}finally(fn) {return this.then((value) => {fn()return value},(reason) => {fn()throw reason})}
}
all和race
- all方法接受一个promise数组作为参数,返回一个新的MyPromise实例。当所有 promise 都成功时,返回一个包含所有成功值的数组;否则,返回一个包含第一个失败的 promise 的错误原因的新的 MyPromise 实例。
- race方法接受一个promise数组作为参数,返回一个新的MyPromise实例。当任何一个promise成功时,返回该成功的promise的值;否则,返回第一个失败的promise的错误原因的新的MyPromise实例。
js
复制代码static all(promises) {return new Promise((resolve, reject) => {if (promises.length === 0) {resolve([])} else {let result = []for (let i = 0;i < promises.length; i++) {promises[i].then(data => {result[i] = dataif (result.length === promises.length) {resolve(result)}},err => {reject(err)return})}}})}static race(promises) {return new Promise((resolve, reject) => {if (promises.length === 0) {resolve()} else {for (let i = 0; i < promises.length; i++) {promises[i].then(data => {resolve(data)},(err) => {reject(err)return})}}})}
完整版MyPromise
js
复制代码
const PENDING = 'pending' // 声明一个常量PENDING,表示Promise的初始状态
const FULFILLED = 'fulfilled' // 声明一个常量FULFILLED,表示Promise的成功状态
const REJECTED = 'rejected' // 声明一个常量REJECTED,表示Promise的失败状态class MyPromise {constructor(executor) {this.state = PENDING // 初始化Promise的状态为PENDINGthis.value = undefined // 初始化Promise的值为undefinedthis.reason = undefined // 初始化Promise的失败原因为undefinedthis.onFulfilledCallbacks = [] // 存储Promise成功状态下的回调函数this.onRejectedCallbacks = [] // 存储Promise失败状态下的回调函数// 定义resolve函数,用于将Promise状态改为FULFILLED,并执行成功状态下的回调函数const resolve = (value) => {if (this.state = PENDING) {this.state = FULFILLED // 将Promise状态改为FULFILLEDthis.value = value // 存储Promise成功时的值this.onFulfilledCallbacks.forEach(fn => fn()) // 执行所有成功状态下的回调函数}}// 定义reject函数,用于将Promise状态改为REJECTED,并执行失败状态下的回调函数const reject = (reason) => {if (this.state === PENDING) {this.state = REJECTED // 将Promise状态改为REJECTEDthis.reason = reason // 存储Promise失败时的原因this.onRejectedCallbacks.forEach(fn => fn()) // 执行所有失败状态下的回调函数}}try {executor(resolve, reject) // 执行executor函数,并传入resolve和reject参数} catch (err) {reject(err) // 捕获错误,并将Promise状态改为REJECTED}}// 静态方法resolve,返回一个状态为FULFILLED的Promise实例static resolve(value) {return new MyPromise((resolve, reject) => {resolve(value)})}// 静态方法reject,返回一个状态为REJECTED的Promise实例static reject(reason) {return new MyPromise((resolve, reject) => {reject(reason)})}// 静态方法all,接收一个包含多个Promise实例的数组,返回一个新的Promise实例static all(promises) {return new Promise((resolve, reject) => {if (promises.length === 0) {resolve([]) // 如果传入的数组为空,则直接返回一个状态为FULFILLED的Promise实例} else {let result = [] // 存储每个Promise实例的执行结果for (let i = 0;i < promises.length; i++) {promises[i].then(data => {result[i] = data // 将每个Promise实例的执行结果存入result数组中if (result.length === promises.length) {resolve(result) // 当所有Promise实例都执行完毕时,返回包含所有结果的新的Promise实例}},err => {reject(err) // 如果其中一个Promise实例执行失败,则将新的Promise实例的状态改为REJECTED,并返回失败原因return})}}})}// 静态方法race,接收一个包含多个Promise实例的数组,返回一个新的Promise实例static race(promises) {return new Promise((resolve, reject) => {if (promises.length === 0) {resolve() // 如果传入的数组为空,则直接返回一个状态为FULFILLED的Promise实例} else {for (let i = 0; i < promises.length; i++) {promises[i].then(data => {resolve(data) // 返回第一个执行完毕的Promise实例的结果},(err) => {reject(err) // 如果其中一个Promise实例执行失败,则将新的Promise实例的状态改为REJECTED,并返回失败原因return})}}})}// then方法,用于在Promise的成功和失败状态下执行回调函数,返回一个新的Promise实例then(onFulfilled, onRejected) {// 如果onFulfilled不是一个函数,则将其更改为返回接收到的值的函数if (typeof onFulfilled !== 'function') {onFulfilled = function (value) {return value}}// 如果onRejected不是一个函数,则将其更改为抛出接收到的原因的函数if (typeof onRejected !== 'function') {onRejected = function (reason) {throw reason}}let promise2 = new MyPromise((resolve, reject) => {switch (this.state) {// 如果Promise当前的状态是PENDING,则将回调函数添加到对应的回调数组中case PENDING:this.onFulfilledCallbacks.push(() => {setTimeout(() => {try {const value = onFulfilled(this.value)resolve(value)} catch (err) {reject(err)}}, 0)})this.onRejectedCallbacks.push(() => {setTimeout(() => {try {const value = onRejected(this.reason)resolve(value)} catch (err) {reject(err)}}, 0)})break// 如果Promise当前的状态是FULFILLED,则直接执行成功回调函数case FULFILLED:setTimeout(() => {try {const value = onFulfilled(this.value)resolve(value)} catch (err) {reject(err)}}, 0)break// 如果Promise当前的状态是REJECTED,则直接执行失败回调函数case REJECTED:setTimeout(() => {try {const value = onRejected(this.reason)resolve(value)} catch (err) {reject(err)}}, 0)break}})return promise2}// catch方法,用于捕获Promise的失败状态,并执行回调函数catch(onRejected) {return this.then(null, onRejected)}// finally方法,无论Promise状态是成功还是失败,都会执行回调函数finally(fn) {return this.then((value) => {fn()return value},(reason) => {fn()throw reason})}
}
学习更多开发知识请关注CRMEB开源商城
相关文章:
手写Promise完整介绍
Promise是一种用于处理异步操作的机制,它可以将异步操作的结果以同步的方式进行处理和返回。在JavaScript中,Promise是一种内置对象,但我们也可以手动实现一个Promise类来更好地理解其原理和工作方式。 Promise的特性 首先,让我…...
【kubernetes系列】Calico原理及配置
概述 Calico是针对容器,虚拟机和基于主机的本机工作负载的开源网络和网络安全解决方案。 Calico支持广泛的平台,包括Kubernetes,OpenShift,Docker EE,OpenStack和裸机服务。 Calico在每个计算节点都利用Linux Kernel实…...
RabbitMQ 的快速使用
docker部署rabbitmq # management才有管理页面 docker pull rabbitmq:management# 新建容器并运行 docker run \-e RABBITMQ_DEFAULT_USERadmin \ -e RABBITMQ_DEFAULT_PASSadmin \ -v mq-plugins:/plugins \--name mq \--hostname mq \-p 15672:15672 \-p 5672:5672 \-itd \ra…...
VUE3添加全局变量
全局变量的添加 在vue3.0中注入全局方法不是在prototype上挂载了,而是添加在config.globalProperties属性上。 //main.js import { createApp } from "vue"; import App from "./App.vue";const app createApp(App); app.config.globalPrope…...
JavaScript基础语法01——初识JavaScript
哈喽,大家好,我是雷工! 最近有项目用到KingFusion软件,由于KingFusion是B/S架构的客户端组态软件,因此在学习KingFusion产品时会涉及许多前端的知识。 像JavaScript语言就是需要用的,俗话说:活到…...
家宽用户家庭网的主要质量问题是什么?原因有哪些
1 引言 截至2020年底,我国家庭宽带(以下简称“家宽”)普及率已达到96%。经过一年多的发展,当前,家庭宽带的市场空间已经饱和。运营商在家宽市场的竞争也随之从新增用户数的竞争转移到家宽品质的竞争。 早期运营商的家…...
ZooKeeper的典型应用场景及实现
文章目录 1、典型应用场景及实现1.1、 数据发布/订阅1.1.1、配置管理案列 1.2、负载均衡1.3、命名服务1.4、分布式协调/通知1.4.1、一种通用的分布式系统机器间通信方式 1.5、集群管理1.6、Master选举1.7、分布式锁1.7.1、排他锁1.7.2、共享锁 1.8、分布式队列 2、ZooKeeper在大…...
智能安全帽~生命体征检测与危险气体检测一体化集成设计还是蓝牙无线外挂式方式好?
生命体征(心率、血氧等)检测&上报平台,危险气体采集&上报平台,是智能安全帽产品中常见的两种选配件,它们的实现有两种典型的模式: 1)将传感器集成到主板上,做成一体化的智能…...
【Java并发】聊聊对象内存布局和syn锁升级过程
对象存储解析:一个空Object对象到底占据多少内存? 对象内存布局 Mark Word占用8字节,类型指针占用8个字节,对象头占用16个字节。 好了,我们来看一下一个Object对占用多少空间, 因为java默认是开启压缩…...
【档案专题】八、电子档案鉴定与销毁
导读:主要针对电子档案鉴定与销毁相关内容介绍。对从事电子档案管理信息化的职业而言,不断夯实电子档案管理相关理论基础是十分重要。只有通过不断梳理相关知识体系和在实际工作当中应用实践,才能走出一条专业化加职业化的道路,从…...
进程与子进程
一、子进程 1.fork()创建子进程 一个现有的进程可以调用 fork()函数创建一个新的进程,调用 fork()函数的进程称为父进程,由 fork()函数创建出来的进程被称为子进程(child process)。(使用该函数需要包含头文件<uni…...
如何对MySQL和MariaDB中的查询和表进行优化-提升查询效率
前言 MySQL和MariaDB是数据库管理系统的流行选择。两者都使用SQL查询语言来输入和查询数据。 尽管SQL查询是简单易学的命令,但并不是所有的查询和数据库函数都具有相同的效率。随着你存储的信息量的增长,如果你的数据库支持一个网站,随着网…...
【Android】关于binder_calls_stats服务
Android 9上有了binder_calls_stats服务,提供了java层的binder统计, Android中的Binder Call Stats(Binder调用统计)是一项用于监控和记录Android系统中Binder通信的统计信息的功能。Binder是Android中的一种进程间通信ÿ…...
给前端返回http链接,由于浏览器缓存不能获取到最新资源怎么办?
1、问题描述 今天在工作中接到这样一个需求,接收前端的图片文件并上传到远程,将原有图片覆盖并返回一个http链接以供前端展示。用户使用后反馈没有修改成功,上了远程拉图片发现已经修改了,但是用户浏览器还是老的图片。排查原因是…...
【Java Web】检查用户登录状态,防止用户访问到非法页面
使用拦截器 在方法前标注自定义注解拦截所有请求,只处理带有该注解的方法 自定义注解: 常用元注解:Target, Rentention, Document, Inherited如何读取注解: - Method.getDeclaredAnnotations() - Method.getAnnotaion(Class<T&…...
数学建模——校园供水系统智能管理
import pandas as pd data1pd.read_excel("C://Users//JJH//Desktop//E//附件_一季度.xlsx") data2pd.read_excel("C://Users//JJH//Desktop//E//附件_二季度.xlsx") data3pd.read_excel("C://Users//JJH//Desktop//E//附件_三季度.xlsx") data4…...
分布式集群——搭建Hadoop环境以及相关的Hadoop介绍
系列文章目录 分布式集群——jdk配置与zookeeper环境搭建 分布式集群——搭建Hadoop环境以及相关的Hadoop介绍 文章目录 前言 一 hadoop的相关概念 1.1 Hadoop概念 补充:块的存储 1.2 HDFS是什么 1.3 三种节点的功能 I、NameNode节点 II、fsimage与edits…...
Python的os.walk()函数使用案例
在Python中,os模块是一个非常实用的工具,它可以让我们与操作系统进行交互,操作文件和目录。在本文中,我们将详细介绍os模块中的遍历文件功能,并通过具体案例和使用场景来解释。 首先,导入os模块。在Pytho…...
学习JAVA打卡第四十五天
StringBuffer类 StringBuffer对象 String对象的字符序列是不可修改的,也就是说,String对象的字符序列的字符不能被修改、删除,即String对象的实体是不可以再发生变化,例如:对于 StringBuffer有三个构造方法ÿ…...
创建K8s pod Webhook
目录 1.前提条件 2.开始创建核心组件Pod的Webhook 2.1.什么是Webhook 2.2.在本地k8s集群安装cert-manager 2.3.创建一个空的文件夹 2.4. 生成工程框架 2.5. 生成核心组件Pod的API 2.6.生成Webhook 2.7.开始实现Webhook相关代码 2.7.1.修改相关配置 2.7.2.修改代码 2…...
Python:操作 Excel 折叠
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...
Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...
MySQL 8.0 OCP 英文题库解析(十三)
Oracle 为庆祝 MySQL 30 周年,截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始,将英文题库免费公布出来,并进行解析,帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...
AI编程--插件对比分析:CodeRider、GitHub Copilot及其他
AI编程插件对比分析:CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展,AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者,分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...
Mac下Android Studio扫描根目录卡死问题记录
环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中,提示一个依赖外部头文件的cpp源文件需要同步,点…...
Linux部署私有文件管理系统MinIO
最近需要用到一个文件管理服务,但是又不想花钱,所以就想着自己搭建一个,刚好我们用的一个开源框架已经集成了MinIO,所以就选了这个 我这边对文件服务性能要求不是太高,单机版就可以 安装非常简单,几个命令就…...
【堆垛策略】设计方法
堆垛策略的设计是积木堆叠系统的核心,直接影响堆叠的稳定性、效率和容错能力。以下是分层次的堆垛策略设计方法,涵盖基础规则、优化算法和容错机制: 1. 基础堆垛规则 (1) 物理稳定性优先 重心原则: 大尺寸/重量积木在下…...
一些实用的chrome扩展0x01
简介 浏览器扩展程序有助于自动化任务、查找隐藏的漏洞、隐藏自身痕迹。以下列出了一些必备扩展程序,无论是测试应用程序、搜寻漏洞还是收集情报,它们都能提升工作流程。 FoxyProxy 代理管理工具,此扩展简化了使用代理(如 Burp…...
相关类相关的可视化图像总结
目录 一、散点图 二、气泡图 三、相关图 四、热力图 五、二维密度图 六、多模态二维密度图 七、雷达图 八、桑基图 九、总结 一、散点图 特点 通过点的位置展示两个连续变量之间的关系,可直观判断线性相关、非线性相关或无相关关系,点的分布密…...
EasyRTC音视频实时通话功能在WebRTC与智能硬件整合中的应用与优势
一、WebRTC与智能硬件整合趋势 随着物联网和实时通信需求的爆发式增长,WebRTC作为开源实时通信技术,为浏览器与移动应用提供免插件的音视频通信能力,在智能硬件领域的融合应用已成必然趋势。智能硬件不再局限于单一功能,对实时…...
