手写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…...
MS5611高精度气压温度传感器Arduino驱动库
1. 项目概述MS5611-Mike-Refactored 是一款面向嵌入式平台(特别是 Arduino 兼容生态)的 MS5611 高精度气压/温度传感器驱动库。该库并非简单封装,而是对 Korneliusz Jarzebski 原始实现的一次系统性重构与工程化增强。其核心目标是将一个基础…...
开发环境配置实战:通过Anaconda Prompt高效管理虚拟环境与Jupyter内核
1. 为什么需要Anaconda Prompt管理虚拟环境 作为数据科学领域的开发者,我经历过无数次Python环境混乱带来的痛苦。记得有一次在交付项目前,突然发现本地运行的模型在服务器上完全无法复现,排查了半天才发现是numpy版本不兼容的问题。这种经历…...
OpenClaw 的模型架构中,是否使用了非自回归生成(NAR)模块?
关于OpenClaw模型架构中是否使用了非自回归生成模块,这其实是一个挺有意思的问题。在讨论具体细节之前,或许可以先聊聊非自回归生成本身在技术演进中的位置。 非自回归生成,也就是NAR,和常见的自回归生成方式不太一样。自回归生成…...
抖音批量下载工具终极指南:免费下载去水印视频的完整教程
抖音批量下载工具终极指南:免费下载去水印视频的完整教程 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browser fallback su…...
现在有开源的最好的矢量动画模型是什么,什么配置的电脑可以部署
目录 一、主流开源矢量动画模型对比 二、电脑配置方案(按预算分级) 1. 旗舰级(稳跑 OmniLottie 高性能) 2. 主流级(稳跑 AnimateDiff 兼顾通用) 3. 入门级(轻量应用) 三、关…...
CSS动画实战:5分钟搞定微信语音发送震动效果(附完整代码)
CSS动画实战:5分钟实现语音波形震动效果 最近在做一个社交类项目时,产品经理突然提出要在语音消息发送时加入波形动画效果,要求"要有微信那种专业感"。作为前端开发者,我第一反应就是:这得用Canvas吧&#x…...
别再只用Chat了!深度挖掘Cursor的‘规则’与‘上下文’功能,打造你的专属AI编程助手
解锁Cursor的隐藏力量:从代码助手到项目级智能架构师 在AI编程工具爆发的时代,大多数开发者仅仅停留在基础对话和代码补全的层面。但Cursor的真正价值远不止于此——它能够成为你项目架构的智能协作者、团队规范的自动化执行者,以及复杂工程问…...
9 鸿蒙页面渲染效率优化实战 | 鸿蒙开发筑基实战
9 鸿蒙页面渲染效率优化实战 | 鸿蒙开发筑基实战 作者:杨建宾(华夏之光永存) 摘要 本文聚焦鸿蒙应用页面渲染卡顿、掉帧、长列表加载缓慢等核心痛点,梳理页面渲染全流程的通用优化方案,从布局规范、组件复用、渲染管控…...
如何使用Photon光影包提升Minecraft视觉体验
如何使用Photon光影包提升Minecraft视觉体验 【免费下载链接】photon A gameplay-focused shader pack for Minecraft 项目地址: https://gitcode.com/gh_mirrors/photon3/photon Photon光影包是一款专注于游戏体验的Minecraft光影解决方案,通过先进的光照算…...
iView组件TypeScript类型推断:提升开发体验的5个高级技巧
iView组件TypeScript类型推断:提升开发体验的5个高级技巧 【免费下载链接】iview A high quality UI Toolkit built on Vue.js 2.0 项目地址: https://gitcode.com/gh_mirrors/iv/iview iView是一个基于Vue.js 2.0的高质量UI组件库,为开发者提供了…...
