手写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…...

抓包-要抓取Spring Boot应用程序的请求
要抓取Spring Boot应用程序的请求,可以按照以下步骤进行操作: 1. 确保你已经按照之前提到的方法设置了Charles代理,并在Charles的SSL代理设置中添加了Spring Boot应用程序的域名。 2. 在Spring Boot应用程序的代码中,添加以下配…...

jmeter+nmon+crontab简单的执行接口定时压测
一、概述 临时接到任务要对系统的接口进行压测,上面的要求就是:压测,并发2000 在不熟悉系统的情况下,按目前的需求,需要做的步骤: 需要有接口脚本需要能监控系统性能需要能定时执行脚本 二、观察 >针…...

ZooKeeper基础命令和Java客户端操作
1、zkCli的常用命令操作 (1)Help (2)ls 使用 ls 命令来查看当前znode中所包含的内容 (3)ls2查看当前节点数据并能看到更新次数等数据 (4)stat查看节点状态 (5…...

【数据分享】2000-2020年全球人类足迹数据(无需转发\免费获取)
人类足迹(Human Footprint)是生态过程和自然景观变化对生态环境造成的压力,是世界各国对生物多样性和生态保护的关注重点。那如何才能获取长时间跨度的人类足迹时空数据呢? 之前我们分享了来自于中国农业大学土地科学与技术学院的城市环境监测及建模&am…...

基于机器学习的fNIRS信号质量控制方法
摘要 尽管功能性近红外光谱(fNIRS)在神经系统研究中的应用越来越广泛,但fNIRS信号处理仍未标准化,并且受到经验和手动操作的高度影响。在任何信号处理过程的开始阶段,信号质量控制(SQC)对于防止错误和不可靠结果至关重要。在fNIRS分析中&…...

分布式锁的三种实现方式是什么?
分布式锁三种实现方式: 基于数据库实现分布式锁;基于缓存(Redis等)实现分布式锁;基于Zookeeper实现分布式锁; 一, 基于数据库实现分布式锁 悲观锁 利用select … where … for update 排他锁…...

华为云软件精英实战营——感受软件改变世界,享受Coding乐趣
机器人已经在诸多领域显现出巨大的商业价值,华为云计算致力于以云助端的方式为机器人产业带来全新机会 如果您是开发爱好者,想了解华为云,想和其他自由开发者交流经验; 如果您是学生,想和正在从事软件开发行业的大佬…...

贪心算法总结篇
文章转自代码随想录 贪心算法总结篇 我刚刚开始讲解贪心系列的时候就说了,贪心系列并不打算严格的从简单到困难这么个顺序来讲解。 因为贪心的简单题可能往往过于简单甚至感觉不到贪心,如果我连续几天讲解简单的贪心,估计录友们一定会不耐…...

ICCV 2023 | 港中文MMLab: 多帧光流估计模型VideoFlow,首次实现亚像素级别误差
本文提出了一个多帧光流估计模型 VideoFlow,旨在充分挖掘视频中的时序信息和运动规律,避免当前主流方法只以两帧图片作为输入而面临的信息瓶颈,显著提升了光流估计的性能。 在公开的 Sintel Bechmark 上,VideoFlow 在 Clean 和 Fi…...

【python爬虫】—图片爬取
图片爬取 需求分析Python实现 需求分析 从https://pic.netbian.com/4kfengjing/网站爬取图片,并保存 Python实现 获取待爬取网页 def get_htmls(pageslist(range(2, 5))):"""获取待爬取网页"""pages_list []for page in pages:u…...