当前位置: 首页 > news >正文

【手写promise——基本功能、链式调用、promise.all、promise.race】

文章目录

  • 前言
  • 一、前置知识
  • 二、实现基本功能
  • 二、实现链式调用
  • 三、实现Promise.all
  • 四、实现Promise.race
  • 总结


前言

关于动机,无论是在工作还是面试中,都会遇到Promise的相关使用和原理,手写Promise也有助于学习设计模式以及代码设计。
本文主要介绍了如何使用自己的代码去实现Promise的一些功能。


以下是本篇文章正文内容

一、前置知识

手写之前,需要知道promise有哪些基本特性:

1、promise有三种状态:pending、fulfilled、rejected。

2、promise的默认状态是pending,状态流转有两种:pending—>fulfilled、pending—>rejected。
在这里插入图片描述

3、实例化Promise时,可以接收一个函数作为参数,这个函数有两个参数,分别是resolve、reject方法用于改变实例状态。并能够执行下一个then中的回调函数。
4、then方法中接收两个函数作为参数,第一个函数相当于执行上一个 promise 中执行 resolve 后对应的回调,第二个函数相当于执行上一个 promise 中执行 reject 后对应的回调。

// 1. 实例化 Promise 时
// 可以接收一个函数作为参数,这个函数可以接收到 resolve 和 reject 两个实例方法
// 用于更改当前实例的状态,并把它们接收到的参数传递给下一个 then 对应的参数中new Promise((resolve, reject) => {// resolve 和 reject 可以都执行,但都执行的意义不大,因为 promise 状态发生更改后,就不能在被更改resolve('ok');// reject('err');}).then((value) => {console.log("resolve callback = ", value); // 若执行 resolve,则 value = ok}, (reason) => {console.log("reject callback = ", reason); // 若执行 reject,则 value = err});

5、then操作后会返回一个新的Promise,且支持链式调用。

let promise = new Promise((resolve,reject) => {resolve(11)
})
const a = new Promise((resolve,reject) => {reject('ok')
})
promise.then(res => {console.log(res)return a
}).then(res => console.log(res))

6、Promise.all以数组的形式接收多个Promise,当所有Promise执行完成,且状态都为fulfilled时,返回执行成功的结果;有一个失败,则返回失败的结果。

7、Promise.race以数组的形式接收多个Promise,只要有一个Promise先执行成功,无论什么状态,都返回这个结果,给到下一个then中对应的回调。

二、实现基本功能

1、

  • new 操作
  • resolve、reject方法
  • then方法
// 定义三种状态
const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';class Promise {constructor(executor) {// 1、默认状态 - PENDINGthis.status = PENDING;// 2、内部维护的变量值this.value = undefined;this.reason = undefined;try {executor(this.resolve.bind(this), this.reject.bind(this)) // 实例化传进来的函数会立即执行} catch(err) {this.reject(err)}}resolve(value) {if (this.status === PENDING) {this.value = value;this.status = FULFILLED;}}reject(reason) {if (this.status === PENDING) {this.reason = reason;this.status = REJECTED;}}then(onFulfilled, onRejected) {if (onFulfilled && this.status === FULFILLED) {let res = onFulfilled(this.value)this.resolvePromise(res, resolve, reject)}if (onRejected && this.status === REJECTED) {let res = onRejected(this.reason)this.resolvePromise(res, resolve, reject)}        }
}

通过以下代码自测:

new Promise((resolve,reject) => {resolve(1)
}).then(res=> {console.log(res) // 1
})

2、考虑异步的情况。
以上代码都是同步的,Promise实例化时传入了异步函数如setTimeout,在setTimeout中resolve,在下一个then的回调函数获取异步函数的状态呢?

我们可以使用回调队列缓存起来,等待执行:

// 定义三种状态
const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';class Promise {constructor(executor) {// 1、默认状态 - PENDINGthis.status = PENDING;// 2、内部维护的变量值this.value = undefined;this.reason = undefined;this.onResolveCallBack = [];// 缓存 onResolve this.onRejectCallBack = [];// 缓存 onReject try {executor(this.resolve.bind(this), this.reject.bind(this)) // 实例化传进来的函数会立即执行} catch(err) {this.reject(err)}}resolve(value) {if (this.status === PENDING) {this.value = value;this.status = FULFILLED;// 遍历调用 onResolveCallBackthis.onResolveCallBack.forEach(fn => fn());}}reject(reason) {if (this.status === PENDING) {this.reason = reason;this.status = REJECTED;this.onResolveCallBack.forEach(fn => fn());}}then(onFulfilled, onRejected) {if (onFulfilled && this.status === FULFILLED) {let res = onFulfilled(this.value)this.resolvePromise(res, resolve, reject)}if (onRejected && this.status === REJECTED) {let res = onRejected(this.reason)this.resolvePromise(res, resolve, reject)}// 当前 promise 状态为 pending,把当前的 onResolve & onReject 缓存起来if (this.status === PENDING) {this.onResolveCallBack.push(() => {onResolve(this.value);});	this.onRejectCallBack.push(() => {onReject(this.value);});}       }
}

用以下代码测试通过:

 let p = new MyPromise((resolve, reject) => {setTimeout(()=>{resolve(1);},1000);}).then(value => {console.log('then resolve = ', value);}, reason => {console.log('then reject = ', reason);});
// then resolve = 1

二、实现链式调用

then(onFulfilled, onRejected) {return new Promise((resolve, reject) => { // 支持链式调用if (onFulfilled && this.status === FULFILLED) {let res = onFulfilled(this.value)resolve(res);}if (onRejected && this.status === REJECTED) {let res = onRejected(this.reason)resolve(res);}if (this.status === PENDING) {this.onResolvedCallbacks.push(() =>{let res = onFulfilled(this.value)resolve(res);})this.onRejectedCallbacks.push(() => {let res = onRejected(this.reason)resolve(res);})}})   }

测试用例:

let promise = new Promise((resolve,reject) => {resolve(1)
})
promise.then(res => {return 2 // 返回普通对象
}).then(res => console.log(res)) // 2

如果在then中返回的是普通对象,上述代码能满足,如果返回的是一个Promise,则还需要补充:

then(onFulfilled, onRejected) {return new Promise((resolve, reject) => { // 支持链式调用if (onFulfilled && this.status === FULFILLED) {let res = onFulfilled(this.value)this.resolvePromise(res, resolve, reject)}if (onRejected && this.status === REJECTED) {let res = onRejected(this.reason)this.resolvePromise(res, resolve, reject)}if (this.status === PENDING) {this.onResolvedCallbacks.push(() =>{let res = onFulfilled(this.value)this.resolvePromise(res, resolve, reject)})this.onRejectedCallbacks.push(() => {let res = onRejected(this.reason)this.resolvePromise(res, resolve, reject)})}})}resolvePromise(res, resolve, reject) {if(res instanceof Promise) { // 对then中返回Promise做处理res.then(resolve, reject)} else{// 普通值resolve(res)}}

测试用例:

let promise = new Promise((resolve,reject) => {resolve(11)
})
const a = new Promise((resolve,reject) => {reject('ok')
})
promise.then(res => {return a;
}).then(res => console.log(res)) // ok

三、实现Promise.all

由上述前置知识可知,Promise.all是全部成功才返回成功的结果,有一个失败就返回失败的结果:

Promise.all = function(arr) {return new Promise((resolve, reject) => {if (!Array.isArray(arr)) {return reject("参数必须为数组");}let successNum = 0; // 成功的Promise条数let resultArray = [];let totalNum = arr.length; // 总Promise数量let isFail = false;arr.forEach(item => {item.then(res => {successNum++;resultArray.push(res);if (successNum === totalNum) { // 说明是全部成功return resolve(resultArray)}}, err => {if (!isFail) reject(err)isFail = true;})});       })
}

测试用例:

function wait500(input) {return new Promise((resolve, reject) => {setTimeout(()=> {resolve(500)}, 500)})
}
function wait1000(input) {return new Promise((resolve, reject) => {setTimeout(()=> {resolve(1000)}, 1000)})
}
// 全部执行完成之后回调
Promise.all([wait1000(), wait500()]).then(result => {console.log('all end', result)
}, err => {console.log('fafaf', err)
}) // 'all end [500, 1000]'

四、实现Promise.race

由上述前置知识可知,Promise.race是有一个promise先执行成功,无论什么状态,都返回这个结果:

Promise.race = function(arr) {return new Promise((resolve, reject) => {if (!Array.isArray(arr)) {return reject("参数必须为数组");}arr.forEach(item => {item.then(res => {return resolve(res); // 状态不可逆,只要其中一个Promise转变了状态,后续状态就不会发生改变}, err => {reject(err);})});       })}

测试用例:

function wait500(input) {return new Promise((resolve, reject) => {setTimeout(()=> {resolve(500)}, 500)})
}
function wait1000(input) {return new Promise((resolve, reject) => {setTimeout(()=> {resolve(1000)}, 1000)})
}
Promise.race([wait1000(), wait500()]).then(result => {console.log('all end', result)
}, err => {console.log('fafaf', err)
}) // 打印出'all end 500'

总结

总的代码:

const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';class Promise {constructor(executor) {// 1、默认状态 - PENDINGthis.status = PENDING;// 2、内部维护的变量值this.value = undefined;this.reason = undefined;// 存放回调this.onResolvedCallbacks = [];this.onRejectedCallbacks = [];try {executor(this.resolve.bind(this), this.reject.bind(this))} catch(err) {this.reject(err)}}resolve(value) {if (this.status === PENDING) {this.value = value;this.status = FULFILLED;this.onResolvedCallbacks.forEach(fn => fn())}}reject(reason) {if (this.status === PENDING) {this.reason = reason;this.status = REJECTED;this.onRejectedCallbacks.forEach(fn => fn())}}then(onFulfilled, onRejected) {return new Promise((resolve, reject) => { // 支持链式调用if (onFulfilled && this.status === FULFILLED) {let res = onFulfilled(this.value)this.resolvePromise(res, resolve, reject)}if (onRejected && this.status === REJECTED) {let res = onRejected(this.reason)this.resolvePromise(res, resolve, reject)}if (this.status === PENDING) {this.onResolvedCallbacks.push(() =>{let res = onFulfilled(this.value)this.resolvePromise(res, resolve, reject)})this.onRejectedCallbacks.push(() => {let res = onRejected(this.reason)this.resolvePromise(res, resolve, reject)})}})}resolvePromise(res, resolve, reject) {if(res instanceof Promise) {res.then(resolve, reject)} else{// 普通值resolve(res)}}
}
// 全部成功才返回成功的结果,有一个失败就返回失败的结果
Promise.all = function(arr) {return new Promise((resolve, reject) => {if (!Array.isArray(arr)) {return reject("参数必须为数组");}let successNum = 0;let resultArray = [];let totalNum = arr.length;let isFail = false;arr.forEach(item => {item.then(res => {successNum++;resultArray.push(res);if (successNum === totalNum) {return resolve(resultArray)}}, err => {if (!isFail) reject(err)isFail = true;})});       })
}// 某个promise先完成,
Promise.race = function(arr) {return new Promise((resolve, reject) => {if (!Array.isArray(arr)) {return reject("参数必须为数组");}arr.forEach(item => {item.then(res => {return resolve(res);}, err => {reject(err);})});       })}

相关文章:

【手写promise——基本功能、链式调用、promise.all、promise.race】

文章目录 前言一、前置知识二、实现基本功能二、实现链式调用三、实现Promise.all四、实现Promise.race总结 前言 关于动机,无论是在工作还是面试中,都会遇到Promise的相关使用和原理,手写Promise也有助于学习设计模式以及代码设计。 本文主…...

计算机网络-笔记-第二章-物理层

目录 二、第二章——物理层 1、物理层的基本概念 2、物理层下面的传输媒体 (1)光纤、同轴电缆、双绞线、电力线【导引型】 (2)无线电波、微波、红外线、可见光【非导引型】 (3)无线电【频谱的使用】 …...

前端开发中的单伪标签清除和双伪标签清除

引言 在前端开发中,我们经常会遇到一些样式上的问题,其中之一就是伪元素造成的布局问题。为了解决这个问题,我们可以使用伪标签清除技术。本篇博客将介绍单伪标签清除和双伪标签清除的概念、用法和示例代码,并详细解释它们的原理…...

云计算中的数据安全与隐私保护策略

文章目录 1. 云计算中的数据安全挑战1.1 数据泄露和数据风险1.2 多租户环境下的隔离问题 2. 隐私保护策略2.1 数据加密2.2 访问控制和身份验证 3. 应对方法与技术3.1 零知识证明(Zero-Knowledge Proofs)3.2 同态加密(Homomorphic Encryption&…...

MacOS软件安装包分享(附安装教程)

目录 一、软件简介 二、软件下载 一、软件简介 MacOS是一种由苹果公司开发的操作系统,专门用于苹果公司的计算机硬件。它被广泛用于创意和专业应用程序,如图像设计、音频和视频编辑等。以下是关于MacOS的详细介绍。 1、MacOS的历史和演变 MacOS最初于…...

【linux进程概念】

目录: 冯诺依曼体系结构操作系统进程 基本概念描述进程-PCBtask_struct-PCB的一种task_ struct内容分类组织进程查看进程 fork()函数 冯诺依曼体系结构 我们常见的计算机,如笔记本。我们不常见的计算机,如服务器,大部分都遵守冯诺…...

直击成都国际车展:远航汽车多款车型登陆车展,打造完美驾乘体验

随着市场渗透率日益高涨,新能源汽车成为今年成都国际车展的关注焦点。在本届车展上,新能源品牌占比再创新高,覆盖两个展馆,印证了当下新能源汽车市场的火爆。作为大运集团重磅打造的高端品牌,远航汽车深度洞察高端智能…...

android nv21 转 yuv420sp

上面两个函数的目标都是将NV21格式的数据转换为YUV420P格式,但是它们在处理U和V分量的方式上有所不同。 在第一个函数NV21toYUV420P_1中,U和V分量的处理方式是这样的:对于U分量,它从NV21数据的Y分量之后的每个奇数位置取数据&…...

使用Nacos与Spring Boot实现配置管理

🌷🍁 博主猫头虎 带您 Go to New World.✨🍁 🦄 博客首页——猫头虎的博客🎐 🐳《面试题大全专栏》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺 &a…...

初识【类和对象】

目录 1.面向过程和面向对象初步认识 2.类的引入 3.类的定义 4.类的访问限定符及封装 5.类的作用域 6.类的实例化 7.类的对象大小的计算 8.类成员函数的this指针 1.面向过程和面向对象初步认识 C语言是面向过程的,关注的是过程,分析出求解问题的…...

软考高级系统架构设计师系列论文八十六:论企业应用集成

软考高级系统架构设计师系列论文八十六:论企业应用集成 一、企业应用集成相关知识点二、摘要三、正文四、总结一、企业应用集成相关知识点 软考高级系统架构设计师系列之:企业集成平台技术的应用和架构设计二、摘要 2022年10月,我参加了***车站综合信息平台项目的开发,承…...

HarmonyOS ArkUI 属性动画入门详解

HarmonyOS ArkUI 属性动画入门详解 前言属性动画是什么?我们借助官方的话来说,我们自己简单归纳下 参数解释举个例子旋转动画 位移动画组合动画总结 前言 鸿蒙OS最近吹的很凶,赶紧卷一下。学习过程中发现很多人吐槽官方属性动画这一章比较敷…...

基于XGBoots预测A股大盘《上证指数》(代码+数据+一键可运行)

对AI炒股感兴趣的小伙伴可加WX:caihaihua057200(备注:学校/公司名字方向) 另外我还有些AI的应用可以一起研究(我一直开源代码) 1、引言 在这期内容中,我们回到AI预测股票,转而探索…...

5G NR:PRACH频域资源

PRACH在频域位置由IE RACH-ConfigGeneric中参数msg1-FrequencyStart和msg1-FDM所指示,其中, msg1-FrequencyStart确定PRACH occasion 0的RB其实位置相对于上行公共BWP的频域其实位置(即BWP 0)的偏移,即确定PRACH的频域起始位置msg1-FDM的取值…...

设计模式——组合模式

什么是组合模式 组合模式(Composite Pattern):组合多个对象形成树形结构以表示具有“整体—部分”关系的层次结构。组合模式对单个对象(即叶子对象)和组合对象(即容器对象)的使用具有一致性,组合模式又可以…...

get属性是什么?有什么用?在什么场景用?get会被Json序列化?

在JavaScript中,对象的属性不仅可以是数据属性(即常规的键值对),还可以是访问器属性(accessor properties)。访问器属性不包含实际的数据值,而是定义了如何获取(get)和设…...

这可能是你看过最详细的 [八大排序算法]

排序算法 前置知识 [排序稳定性]一、直接插入排序二、希尔排序三、直接选择排序四、堆排序五、冒泡排序六、快速排序七、归并排序八、计数排序(非比较排序)排序复杂度和稳定性总结 前置知识 [排序稳定性] 假定在待排序的记录序列中,存在多个…...

docker的安装

CentOS7 安装 Docker 安装需要的软件包, yum-util 提供yum-config-manager功能,另两个是devicemapper驱动依赖 yum install -y yum-utils device-mapper-persistent-data lvm2 添加下载源 yum-config-manager --add-repo http://mirrors.aliyun.com/…...

【业务功能篇75】微服务项目环境搭建docker-mysql-redisSpringCloudAlibaba

项目环境准备 1.虚拟机环境 我们可以通过VMWare来安装,但是通过VMWare安装大家经常会碰到网络ip连接问题,为了减少额外的环境因素影响,Docker内容的讲解我们会通过VirtualBox结合Vagrant来安装虚拟机。 VirtualBox官网:https:/…...

学习笔记|认识数码管|控制原理|数码管实现0-9的显示|段码跟位码|STC32G单片机视频开发教程(冲哥)|第九集:数码管静态显示

文章目录 1.认识数码管2.控制原理十进制转换为任意进制其它进制转十进制 3.数码管实现0-9的显示1.用数组定义0-9的内码段码跟位码的区别2.尝试用延时实现0-9的循环显示3.用按键控制数字的加或者减。 总结课后练习: 1.认识数码管 数码管按段数可分为七段数码管和八段…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)

2025年能源电力系统与流体力学国际会议(EPSFD 2025)将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会,EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:

在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档&#xff0c…...

对WWDC 2025 Keynote 内容的预测

借助我们以往对苹果公司发展路径的深入研究经验,以及大语言模型的分析能力,我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际,我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测,聊作存档。等到明…...

Linux-07 ubuntu 的 chrome 启动不了

文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了,报错如下四、启动不了,解决如下 总结 问题原因 在应用中可以看到chrome,但是打不开(说明:原来的ubuntu系统出问题了,这个是备用的硬盘&a…...

成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战

在现代战争中,电磁频谱已成为继陆、海、空、天之后的 “第五维战场”,雷达作为电磁频谱领域的关键装备,其干扰与抗干扰能力的较量,直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器,凭借数字射…...

JS手写代码篇----使用Promise封装AJAX请求

15、使用Promise封装AJAX请求 promise就有reject和resolve了,就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...

Linux系统部署KES

1、安装准备 1.版本说明V008R006C009B0014 V008:是version产品的大版本。 R006:是release产品特性版本。 C009:是通用版 B0014:是build开发过程中的构建版本2.硬件要求 #安全版和企业版 内存:1GB 以上 硬盘&#xf…...

学习一下用鸿蒙​​DevEco Studio HarmonyOS5实现百度地图

在鸿蒙(HarmonyOS5)中集成百度地图,可以通过以下步骤和技术方案实现。结合鸿蒙的分布式能力和百度地图的API,可以构建跨设备的定位、导航和地图展示功能。 ​​1. 鸿蒙环境准备​​ ​​开发工具​​:下载安装 ​​De…...

基于鸿蒙(HarmonyOS5)的打车小程序

1. 开发环境准备 安装DevEco Studio (鸿蒙官方IDE)配置HarmonyOS SDK申请开发者账号和必要的API密钥 2. 项目结构设计 ├── entry │ ├── src │ │ ├── main │ │ │ ├── ets │ │ │ │ ├── pages │ │ │ │ │ ├── H…...

基于开源AI智能名片链动2 + 1模式S2B2C商城小程序的沉浸式体验营销研究

摘要:在消费市场竞争日益激烈的当下,传统体验营销方式存在诸多局限。本文聚焦开源AI智能名片链动2 1模式S2B2C商城小程序,探讨其在沉浸式体验营销中的应用。通过对比传统品鉴、工厂参观等初级体验方式,分析沉浸式体验的优势与价值…...