我这样实现Promise

news2024/10/2 12:36:50
一、剖析Promise的基础框架

要实现Promise原理,肯定得先明白其原理。Promsie的基础框架如下:

// 挂在原型上的方法,实例对象可以访问并且使用
MyPromise.prototype.then = function(onResolved, onRejected) {//.then接收两个函数类型的形参,分别处理Promise为成功和失败状态时的情况
}
MyPromise.prototype.catch = function(onRejected) {//.catch接收一个函数类型的形参,相当于.then的第二个回调函数
}
//...还有一些其他方法
function MyPromise(executor) {// Promise的初始状态为pendinglet self = this;this.status = 'pending'//executor包含两个函数类型的形参,即resolve和rejectfunction resolve() {}
​function reject() {}
​//其他具体操作
} 
二、实现Promsie中两个类型为函数的形参

我们首先要明白的是:

1、Promise的三种状态

(1)pending(待处理):Promise的初始状态 (2)fufilled(处理成功):Promise执行完resolve回调后的状态 (3)rejected(处理失败):Promise执行完reject回调后的状态 当Promise状态为pending状态的时候,为同步代码,如果后面接.then,不会执行它的回调函数; 当状态为resolved和rejected的时候,为异步代码,可用定时器模拟实现。

2、Promise的两个类型为函数的形参

(1)resolve:调用resolve函数,Promise状态变为成功

(2)reject:调用reject函数,Promise状态变为失败

有了以上的知识基础,是不是就更好下手了。

(function(window) {function MyPromise(executor) {let self = this //保存this对象self.status = 'pending' //初始状态为pendingself.data = undefinedself.callbacks = []
​// 实现resolvefunction resolve(value) {//必须是pending状态,Promsie的状态只会被改变一次if (self.status !== 'pending') {return}// 将状态变为fullfilledself.status = 'fullfilled'self.data = value
​//有没有待执行的回调函数if (self.callbacks.length > 0) {//如果有,必须都执行
                setTimeout(() => {//定时器模拟异步
                    self.callbacks.forEach(callbackObj => {
                            callbackObj.onResolved(value)
                  })})}}
​function reject(value) {// 同样也必须是pending状态if (self.status !== 'pending') {return}// 将状态变为rejectedself.status = 'rejected'self.data = value
​// 有没有待执行的回调if (self.callbacks.length > 0) {//如果有,必须都执行
                setTimeout(() => {//定时器模拟异步self.callbacks.forEach(callbackObj => {callbackObj.onRejected(value)//失败的回调})})}}
        
        try {executor(resolve, reject)} catch (error) {reject(error)}}window.MyPromise = MyPromise
})(window) 

简单概括,实现resolve和reject步骤,

(1)判断状态是否为‘pending’,必须是’pending’状态,因为Promise的状态只会改变一次,如果执行resolve和reject的时候,状态不是’pending’状态,那么它的状态是不会再被改变的。如果状态仍然为Promsie的初始状态’pending’,则进行下一步操作;否则,如果不是,直接跳过,不需要任何返回值。

(2)如果是’pending’状态,将状态改变为成功或者失败状态,同时将数据包裹传递,让.then或者.catch执行的时候,可以得到返回值;

(3)判断是否还有未执行的回调函数,一般是指.then或者.catch里面的还未执行的回调函数,如果有的话,必须都执行,同时他都是异步代码,使用定时器模拟异步效果。

(4)最后,使用*try{}catch(){}*模块,try里面执行这个Promise的异步函数,如果有异常,统一按照rejected状态处理错误。

三、实现.then

(1)then接收两个函数类型的参数,第一个是Promise成功情况的回调,第二个是失败情况的回调;当Promise状态为成功时执行第一个回调函数,当Promsie状态为失败时执行第二个回调函数。

(2)Promise的.then是微任务

(3)Promise的.then可以链式调用

(4).then返回的是一个Promise对象

.then是Promise原型上的,所以可以将它挂载原型上实现:

MyPromise.prototype.then = function(onResolved, onRejected) {// 把回调用对象包裹,存放在callbacks中let self = thisreturn new MyPromise((resolve, reject) => {if (self.status === 'pending') { //状态还未改变,.then有两个回调函数self.callbacks.push({onResolved,onRejected})} else if (self.status === 'fullfilled') { //状态为成功,执行onResolved//定时器模拟.then的异步执行setTimeout(() => {const result = onResolved(self.data)if (result instanceof MyPromise) { //返回值是Promise对象result.then( //为了将result的状态变更成resolved(res) => {resolve(res)},(err) => {reject(err)})return result} else { //返回值不是Promise对象resolve(result)}})} else { //状态为成功,执行onRejectedsetTimeout(() => {onRejected(self.data)})}})
} 

实现.then最大的难点就是它的链式调用,如果返回值是一个Promise对象,就执行.then里面的操作;但是如果返回值不是Promsie对象,那就直接调用Promise的resolve函数。另外,还需要明确this的指向,这里this指向的不是新生成的Pomise对象,这点比较重要。

四、实现.catch

(1).catch回调相当于.then的第二个回调,可以捕获reject出来的错误或者直接在Promsie内部throw new Error()出来的错误。它的实现相对比较简单,如果状态为rejected,我们就执行onRejected函数,同时也需要使用定时器模拟异步。

MyPromise.prototype.catch = function(onRejected) {let self = thisif (self.status == 'pending') {self.callbacks.push({onRejected})} else if (self.status == 'rejected') {setTimeout(() => {onRejected(self.data)})}
} 

除了.then和.catch方法是挂载在Promise原型上的回调函数,还有Promise.prototype.finally();另外Promise.all(),Promise.race()。。。不是原型上的方法。感兴趣的可以尝试写出他们的实现原理。

五、测试
1、resolve

Promsie内部没有任何其他return情况,测试如下:

let promise = new MyPromise((resolve, reject) => {resolve('成功啦~')
}).then(res => {console.log(res); //成功啦~
}) 
2、reject

测试reject,结果如下:

let promise = new MyPromise((resolve, reject) => {reject('失败啦~')
}).then(res => {console.log(res);
}, err => {console.log(err);//失败啦~
}) 
3、.then

测试.then,结果如下

let MyPromiseTest = new MyPromise((resolve, reject) => {resolve('成功啦');}).then(res => {console.log(res);
​}).then(res => {console.log('猜猜我打印什么');})
//成功啦
//猜猜我打印什么 
4、.catch
let promise = new MyPromise((resolve, reject) => {reject('失败啦')
}).catch(err => {console.log(err);//失败啦
}) 

以上实现原理只能在浏览器执行成功,因为node端没有window对象;如果想在node端实现Promie原理,最推荐和优雅的方式就是使用ES6里面的class(类),使用类的话,原型上的方法就写在constructor里面,而其他方法不是原型上的,是类的静态方法,就不能被类的实例对象继承,需要使用关键字static表示,其余的逻辑都一样的哦~。

最后

整理了一套《前端大厂面试宝典》,包含了HTML、CSS、JavaScript、HTTP、TCP协议、浏览器、VUE、React、数据结构和算法,一共201道面试题,并对每个问题作出了回答和解析。

有需要的小伙伴,可以点击文末卡片领取这份文档,无偿分享

部分文档展示:



文章篇幅有限,后面的内容就不一一展示了

有需要的小伙伴,可以点下方卡片免费领取

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/369836.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Elasticsearch7.8.0版本进阶——文档分析 分析器

目录一、文档分析过程二、分析器三、内置分析器3.1、标准分析器3.2、简单分析器3.3、空格分析器3.4、语言分析器四、分析器使用场景五、分析器的测试示例一、文档分析过程 将一块文本分成适合于倒排索引的独立的词条。将这些词条统一化为标准格式以提高它们的“可搜索性”&…

RFID在产线上的作用

RFID在产线上的作用RFID技术应用于产线监控,可以实现产线的自动控制和检测。RFID读写器每识别一个产品标签,就可以将数据传输到电脑程序中,记录每1件产品的原料和来源、生产线位置、生产过程和库存状况等信息,为企业更好地管理生产…

实现基于国密SM3的密钥派生(KDF)功能

实现基于国密SM3的密钥派生(KDF)前言KDF 标准基于SM3的kdf实现前言 密钥派生函数(KDF):密钥派生函数是指从一个共享的秘密比特串中派生密钥数据,在密钥协商过程中,密钥派生函数作用在密钥交换所…

davis2016评估教程

DAVIS 2016是VOS任务中的一个经典的benchmark,但是一些VOT的算法有时候也可以预测mask,所以也会在上面测一测性能,本次就随手记录一下自己评测的过程,有需要的小伙伴可以往下看。 DAVIS 2016数据集官方项目网站:https:…

【微信小程序】-- 常用的基础内容组件介绍 -- text rich-text progress icon(七)

💌 所属专栏:【微信小程序开发教程】 😀 作  者:我是夜阑的狗🐶 🚀 个人简介:一个正在努力学技术的CV工程师,专注基础和实战分享 ,欢迎咨询! &#…

获取Windows11开发环境及VirtualBox配置指南

今天我们来讲一讲Windows11开发环境的快速搭建,主要是通过Virtualbox虚拟机安装微软官方预先配置好的Windows11环境包,配置简单,开箱即用。 获取虚拟机打包镜像 微软官方提供了多个系统平台的Windows11虚拟机镜打包镜像,只需要导…

维格云社区版APItable

目录 ✨ 快速开始 🔥 功能特性 💥 应用场景 💞 面向 API 💝 嵌入友好 安装 🧑‍💻 贡献 开发环境 Git 工作流基础 工作约定

[零刻] SER5 Pro 迷你主机:从开箱到安装ESXi+虚拟机

开箱先上图: SER5Pro这个小主机采用了AMD580H的处理器,性能相当强悍,用来做All in one主机非常合适,特别是独有的背面散热系统,可以同时给内存和硬盘散热,在长期运行下散热完全不用担心稳定性,放…

揭穿数据分析的六大谎言

目前许多企业在决策时仍沿用以往的个人经验,没有用数据说话,这在实际决策运行时会出现很多问题。在数据分析行业发展成熟的国家,90%的市场决策和经营决策都是通过数据分析研究确定的。用数据说话,重视定量分析,也逐渐成…

十年测试大佬教你如何从零到一落地接口自动化测试?

目录 为什么要做接口测试 理解接口和接口测试 如何落地接口自动化测试 总结 重点:配套学习资料和视频教学 为什么要做接口测试 测试理念的演变 早些时候,软件研发交付流程大多遵循V型或W型的瀑布模式,这种模式下只有开发编码完成才会提测…

学生白嫖阿里服务器

测试答案,直接CtrlF查找即可 WEB2.0时代黑客攻击的主要目标集中在(A) A. 互联网应用 B. 穿透防火墙 C. 破坏操作系统 D. 计算机硬件 以下常见的通讯协议中,不属于应用层协议的有(B) A. FTP B. TCP/IP C. HT…

腾讯云服务器部署onnxruntime-gpu经验总结

前言 有项目需要用onnxruntime-gpu进行推理,原以为像windows一样在已经有cuda的情况下直接安装onnxruntime-gpu即可,却没想到这么麻烦,故分享此文帮助后来者。 环境 gpu计算型英伟达v100云服务器。 在选择安装系统时已经选择了最高版本如下…

嵌入式 STM32 实现STemwin移植+修改其配置文件,驱动LCD显示文本 (含源码,建议收藏)

目录 一、STemwin 简介 二、源码下载 1、在移植STemwin源码之前,需要一个已经具备LCD读写,填充指定颜色等函数功能的一个工程; 2、STemwin 3、源码下载 三、STemwin移植 1、解压源码路径 2、STemwin文件介绍 四、修改配置文件&…

【PyTorch】教程:DCGAN

DCGAN 本教程将通过一个示例来介绍 DCGAN。 我将训练一个生成对抗网络 (GAN) ,在向其展示许多真实名人的照片后生成新的名人。这里大部分代码来自于 pytorch/examples 。本文档针对这些实现进行全面解释,并阐述该模型的工作方式和…

在windows搭建Redis集群并整合入Springboot项目

搭建集群配置规划Redis集群编写bat来启动每个redis服务安装Ruby安装Redis的Ruby驱动出现错误镜像过期SSL证书过期安装集群脚本redis-trib启动每个节点并执行集群构建脚本测试搭建是否成功配置springboot项目中配置规划Redis集群 我们搭建三个节点的集群,每个节点有…

骨传导耳机推荐哪款好,列举几款是市面上热销的骨传导耳机

​骨传导耳机是一种新型的耳机类型,通过震动和声音将振动传到了耳道外,对耳道不会产生损伤,能够保护听力。相比于传统耳机的优势有很多,比如运动时佩戴更加稳固,也可以在听歌时与人交谈。但在市面上的骨传导耳机款式可…

无重叠区间-力扣435-java贪心策略

一、题目描述给定一个区间的集合 intervals ,其中 intervals[i] [starti, endi] 。返回 需要移除区间的最小数量,使剩余区间互不重叠 。示例 1:输入: intervals [[1,2],[2,3],[3,4],[1,3]]输出: 1解释: 移除 [1,3] 后,剩下的区间没有重叠。…

Spring MVC 源码- HandlerAdapter 组件(四)之 HandlerMethodReturnValueHandler

HandlerAdapter 组件HandlerAdapter 组件,处理器的适配器。因为处理器 handler 的类型是 Object 类型,需要有一个调用者来实现 handler 是怎么被执行。Spring 中的处理器的实现多变,比如用户的处理器可以实现 Controller 接口或者 HttpReques…

服务器部署—XShell连接阿里云服务器,linux系统里面数据库访问乱码怎么办?

我是用的xshell连接的云服务器,今天想在服务器上面部署一个项目,但是当我在数据库里面安装mysql之后,通过select的sql语句查询数据,在表里面的中文出现乱码,给我直接干懵了,这个怎么办?而且还有…

【项目精选】jsp网上招标系统(视频+源码+论文)

点击下载源码 威客理论的起源 威客理论的提出基于其创始人刘锋发现的三个基石:发现电子公告牌功能分离现象;确认互联网知识价值化时代的到来、互联网是人类大脑的联网而不是仅仅为机器的联网。 1、电子公告牌功能分离现象的发现  2005年6月威客&#xf…