手写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…...
椭圆曲线密码学(ECC)
一、ECC算法概述 椭圆曲线密码学(Elliptic Curve Cryptography)是基于椭圆曲线数学理论的公钥密码系统,由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA,ECC在相同安全强度下密钥更短(256位ECC ≈ 3072位RSA…...

【JavaEE】-- HTTP
1. HTTP是什么? HTTP(全称为"超文本传输协议")是一种应用非常广泛的应用层协议,HTTP是基于TCP协议的一种应用层协议。 应用层协议:是计算机网络协议栈中最高层的协议,它定义了运行在不同主机上…...

23-Oracle 23 ai 区块链表(Blockchain Table)
小伙伴有没有在金融强合规的领域中遇见,必须要保持数据不可变,管理员都无法修改和留痕的要求。比如医疗的电子病历中,影像检查检验结果不可篡改行的,药品追溯过程中数据只可插入无法删除的特性需求;登录日志、修改日志…...
java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别
UnsatisfiedLinkError 在对接硬件设备中,我们会遇到使用 java 调用 dll文件 的情况,此时大概率出现UnsatisfiedLinkError链接错误,原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用,结果 dll 未实现 JNI 协…...
Golang dig框架与GraphQL的完美结合
将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用,可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器,能够帮助开发者更好地管理复杂的依赖关系,而 GraphQL 则是一种用于 API 的查询语言,能够提…...
Nginx server_name 配置说明
Nginx 是一个高性能的反向代理和负载均衡服务器,其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机(Virtual Host)。 1. 简介 Nginx 使用 server_name 指令来确定…...
今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存
文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...

嵌入式学习笔记DAY33(网络编程——TCP)
一、网络架构 C/S (client/server 客户端/服务器):由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序,负责提供用户界面和交互逻辑 ,接收用户输入,向服务器发送请求,并展示服务…...

数据结构第5章:树和二叉树完全指南(自整理详细图文笔记)
名人说:莫道桑榆晚,为霞尚满天。——刘禹锡(刘梦得,诗豪) 原创笔记:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 上一篇:《数据结构第4章 数组和广义表》…...
Android屏幕刷新率与FPS(Frames Per Second) 120hz
Android屏幕刷新率与FPS(Frames Per Second) 120hz 屏幕刷新率是屏幕每秒钟刷新显示内容的次数,单位是赫兹(Hz)。 60Hz 屏幕:每秒刷新 60 次,每次刷新间隔约 16.67ms 90Hz 屏幕:每秒刷新 90 次,…...