手写Promise核心代码

news/2025/10/6 17:32:26/文章来源:https://www.cnblogs.com/lushuang55/p/19127680

目录
  • 初始结构
  • 坑点:this指向问题
  • then
  • 执行异常
  • then的参数
  • 异步
  • 回调保存
  • 坑点:resolve和reject在事件循环末尾执行
  • 链式调用
  • 完整代码

初始结构

  • 原生的promise使用new创建一个实例,传入的参数是一个函数,会自动执行。原生的Promise可以传入resolve和reject两个参数。调用resolve是以函数形式调用的:resolve(),可以传入参数resolve('okok')

  • promise有三种状态:Pending(进行中)、Fulfilled(已成功)、Rejected(已失败)。一个 Promise 对象只能从 Pending 状态转换到 Fulfilled 或 Rejected 状态,并且状态转换是不可逆的。

let promise = new Promise((resolve,reject) =>{resolve('okok');
});
class MyPromise{static PENDING = '待定'; static FULFILLED='成功'; static REJECTED = '失败';constructor(func){this.status = MyPromise.PENDING;this.result = null;//每个实例都有result属性func(this.resolve, this.reject);}resolve(result){if(this.status === MyPromise.PENDING){this.status = this.FULFILLEDthis.result = result;//把参数赋值给实例的result属性}}reject(result){if(this.status === MyPromise.PENDING){this.status = this.REJECTED;this.result = result}}
}

坑点:this指向问题

  • 上面代码执行结果:
    image

  • 分析原因:new一个新实例的时候执行的是constructor里的内容,在新实例被创建后再在外部环境下执行resolve,相当于不在class内部使用这个this,外部没有所有会报undefined。

  • 解决办法:使用bind绑定:固定 this指向,确保 resolve和 reject的 this始终是 MyPromise实例

class MyPromise{static PENDING = '待定'; static FULFILLED='成功'; static REJECTED = '失败';constructor(func){this.status = MyPromise.PENDING;this.result = null;func(this.resolve.bind(this), this.reject.bind(this));}resolve(result){if(this.status === MyPromise.PENDING){this.status = this.FULFILLEDthis.result = result}}reject(result){if(this.status === MyPromise.PENDING){this.status = this.REJECTED;this.result = result}}
}

then

  • then方法可以传入两个参数。两个参数都是函数。一个是当状态为成功时执行的代码,一个是当状态为失败时执行的代码
let promise = new Promise((resolve,reject) =>{resolve('okok');reject('nono');
});
promise.then(result => {console.log(result)},(error) => {console.log(error.message);}
);

image

  • 执行结果只有一个,所以手写时进行判断。如果当前实例的状态为成功的话,执行传进来的onFULFILLED函数,并传入前面保留的result属性值;错误类似
class MyPromise {static PENDING = "待定";static FULFILLED = "成功";static REJECTED = "失败";constructor(func) {this.status = MyPromise.PENDING;this.result = null;func(this.resolve.bind(this), this.reject.bind(this));}resolve(result) {if (this.status === MyPromise.PENDING) {this.status = this.FULFILLED;this.result = result;}}reject(result) {if (this.status === MyPromise.PENDING) {this.status = this.REJECTED;this.result = result;}}then(onFULFILLED, onREJECTED) {if (this.status === MyPromise.FULFILLED) {onFULFILLED(this.result);}if (this.status === MyPromise.REJECTED) {onREJECTED(this.result);}}
}

执行异常

  • 执行函数里面抛出错误是会触发拒绝方法的。调用then可以把错误信息作为内容输出出来

image

  • 手写实现同样功能。在执行resolve和reject前进行判断,如果有报错就把错误信息传给reject方法,并直接执行reject方法。这里不用this绑定:这里是直接执行,不是创建实例后再执行。
constructor(func) {this.status = MyPromise.PENDING;this.result = null;try{func(this.resolve.bind(this), this.reject.bind(this))}catch(error){this.reject(error)}}

then的参数

  • 原生promise里规定then的两个参数如果不是函数的话就要忽略

image

  • then函数中使用条件运算符:把不是函数的参数改为函数
then(onFULFILLED, onREJECTED) {onFULFILLED = typeof onFULFILLED === 'function' ? onFULFILLED : ()=>{}onREJECTED = typeof onREJECTED === 'function' ? onREJECTED : ()=>{}if (this.status === MyPromise.FULFILLED) {onFULFILLED(this.result);}if (this.status === MyPromise.REJECTED) {onREJECTED(this.result);}}

异步

  • 原生的异步执行顺序:
    image

  • 给手写的代码在then方法里面添加setTimeout就可以了,需要在if判断状态符合再添加异步

then(onFULFILLED, onREJECTED) {onFULFILLED = typeof onFULFILLED === "function" ? onFULFILLED : () => {};onREJECTED = typeof onREJECTED === "function" ? onREJECTED : () => {};if (this.status === MyPromise.FULFILLED) {setTimeout(() => {onFULFILLED(this.result);});}if (this.status === MyPromise.REJECTED) {setTimeout(() => {onREJECTED(this.result);});}}

回调保存

  • 原生
    image

  • 手写
    image

  • 手写的结果对比原生差了resolve的执行的okok。原因:then方法里面是根据条件判断来执行的代码,没有符合的状态就不会执行。then里面没有定义待定状态应该做什么。

  • 解决:then方法中添加待定的情况。这个时候resolve和reject还没获取到任何值。必须让then稍后执行,等resolve执行了再执行。1、为了保留then里的函数,创建数组来保存函数,在实例化的时候就让每个实例都有这两个数组。一个保存resolve函数,一个保存reject函数。2、当状态是待定时,把then里的两个参数放在两个数组里。3、执行resolve/reject时遍历自身的callbacks数组,有then保存过来的待执行的函数逐个执行。

class MyPromise{static PENDING = '待定'; static FULFILLED='成功'; static REJECTED = '失败';constructor(func){this.status = MyPromise.PENDING;this.result = null;this.resolveCallbacks = [];//第一步this.rejectCallbacks = [];//第一步try{func(this.resolve.bind(this), this.reject.bind(this))}catch(error){this.reject(error)}}resolve(result){if(this.status === MyPromise.PENDING){this.status = this.FULFILLEDthis.result = resultthis.resolveCallbacks.forEach(callback => {callback(result)})//第三步}}reject(result){if(this.status === MyPromise.PENDING){this.status = this.REJECTED;this.result = resultthis.rejectCallbacks.forEach(callback => {callback(result)})//第三步}}then(onFULFILLED, onREJECTED){onFULFILLED = typeof onFULFILLED === 'function' ? onFULFILLED : ()=>{}onREJECTED = typeof onREJECTED === 'function' ? onREJECTED : ()=>{}if(this.status === MyPromise.PENDING){//第二步this.resolveCallbacks.push(onFULFILLED);this.rejectCallbacks.push(onREJECTED);}if(this.status === MyPromise.FULFILLED){setTimeout(()=>{onFULFILLED(this.result)})}if(this.status === MyPromise.REJECTED){setTimeout(()=>{onREJECTED(this.result)})}}
}

坑点:resolve和reject在事件循环末尾执行

上面代码的执行结果是:
image

  • resolve是异步,应该先输出“第四步”,解决:只需要给resolve和reject里面加上setTimeout即可
resolve(result){setTimeout(()=>{if(this.status === MyPromise.PENDING){this.status = this.FULFILLEDthis.result = resultthis.resolveCallbacks.forEach(callback => {callback(result)})}});}reject(result){setTimeout(()=>{if(this.status === MyPromise.PENDING){this.status = this.REJECTED;this.result = resultthis.rejectCallbacks.forEach(callback => {callback(result)})}})}

链式调用

  • then中返回一个新的手写Promise实例
 then(onFULFILLED, onREJECTED){return new MyPromise((resolve,reject)=>{onFULFILLED = typeof onFULFILLED === 'function' ? onFULFILLED : ()=>{}onREJECTED = typeof onREJECTED === 'function' ? onREJECTED : ()=>{}if(this.status === MyPromise.PENDING){this.resolveCallbacks.push(onFULFILLED);this.rejectCallbacks.push(onREJECTED);}if(this.status === MyPromise.FULFILLED){setTimeout(()=>{onFULFILLED(this.result)})}if(this.status === MyPromise.REJECTED){setTimeout(()=>{onREJECTED(this.result)})}})  }

完整代码

class MyPromise{static PENDING = '待定'; static FULFILLED='成功'; static REJECTED = '失败';constructor(func){this.status = MyPromise.PENDING;this.result = null;this.resolveCallbacks = [];this.rejectCallbacks = [];try{func(this.resolve.bind(this), this.reject.bind(this))}catch(error){this.reject(error)}}resolve(result){setTimeout(()=>{if(this.status === MyPromise.PENDING){this.status = this.FULFILLEDthis.result = resultthis.resolveCallbacks.forEach(callback => {callback(result)})}});}reject(result){setTimeout(()=>{if(this.status === MyPromise.PENDING){this.status = this.REJECTED;this.result = resultthis.rejectCallbacks.forEach(callback => {callback(result)})}})}then(onFULFILLED, onREJECTED){return new MyPromise((resolve,reject)=>{onFULFILLED = typeof onFULFILLED === 'function' ? onFULFILLED : ()=>{}onREJECTED = typeof onREJECTED === 'function' ? onREJECTED : ()=>{}if(this.status === MyPromise.PENDING){this.resolveCallbacks.push(onFULFILLED);this.rejectCallbacks.push(onREJECTED);}if(this.status === MyPromise.FULFILLED){setTimeout(()=>{onFULFILLED(this.result)})}if(this.status === MyPromise.REJECTED){setTimeout(()=>{onREJECTED(this.result)})}})  }
}

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

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

相关文章

手动数据库分库分片策略

手动数据库分片// 手动分片逻辑示例 func getShardDB(userID int) *gorm.DB {dbIndex := userID % 4dsn := fmt.Sprintf("user:pwd@tcp(db-%d:3306)/user_db_%d", dbIndex, dbIndex)db, _ := gorm.Open(mysq…

大数据分析公司季度业绩与技术进展

某大数据分析公司公布季度财报,营收同比增长48%,超出预期。文章重点介绍了其AI驱动技术平台在国防和供应链领域的应用,包括实时战场分析系统和AI原生制造能力等技术架构。财务业绩亮点调整后每股收益16美分,高于去…

十大素材网站百度企业网站建设费用

epoll默认情况下是水平触发模式,这次将epoll设置为边缘触发模式来实现服务器,而客户端直接使用完美回声服务器的客户端。服务器代码#include #include #include #include #include #include #include #include #include const int BUF_SIZE 4;const int…

tmux 终端复用器教程,创建一个持久的会话

下面这个表格对比了两种主流方法的核心操作,你可以快速了解: 特性 tmux (推荐) screen 创建新会话 tmux new-session -s <session_name> screen -S <session_name> 分离会话 Ctrl + B, 松开后按 D Ctrl …

理解Transformer中的位置编码

要理解Transformer中的位置编码(Positional Encoding),核心是先搞懂「为什么需要它」,再用「直观比喻+关键特性」拆解它的设计逻辑,全程避开复杂公式,聚焦「它到底在做什么」。 一、先解决核心问题:为什么需要位…

完整教程:android stdio 的布局属性

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

南阳集团网站建设互联网公司营业执照经营范围

ROS2 — quaternion_inverse() quaternion_inverse() 函数用于计算四元数的逆。四元数是一种在三维空间中表示旋转的数学工具&#xff0c;它由一个实部和三个虚部组成&#xff0c;通常表示为 q w xi yj zk&#xff0c;其中 w 是实部&#xff0c;而 x, y, z 是虚部&#xf…

建设网站哪里好青岛企业展厅设计公司

你们呼唤了无数次的妙记多 Mojidoc PC客户端 Beta版本正式上线啦&#xff01; 感谢300位妙友积极参与内测&#xff0c;给予了我们很多非常有效的意见和建议&#xff01;我们会根据用户反馈不断优化和修复相关功能&#xff0c;在此感谢妙友们一直以来的支持&#xff5e; PC端拥…

网络风险管理的三大关键洞察

本文深入探讨网络风险管理的三大核心洞察:从攻击者视角审视攻击面、基于风险优先级进行漏洞修复,以及通过AI自动化实现主动风险管理。这些策略能帮助企业从被动防御转向主动安全,有效提升整体安全防护水平。网络风险…

鸿蒙UI研发——组件的自适应拉伸

鸿蒙UI研发——组件的自适应拉伸2025-10-06 17:21 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !import…

网站建设常见问题解决方案wordpress主题手机版不显示侧边栏

这篇文章将详细介绍 如何在 stm32103 板子上点亮一个LED. 文章目录 前言一、开发环境搭建。二、LED 原理图解读三、什么是 GPIO四、cubeMX 配置工程五、解读 cubeMX 生成的代码六、延时函数七、控制引脚状态函数点亮 LED 八、GPIO 的工作模式九、为什么使用推挽输出驱动 LED总结…

网站备案查询 站长传奇手游发布网站

功能配置 一、用户 用户管理小黑屋用户反馈登录设置短信参数 二、作品 视频作品背景音乐库背景音乐分类 三、形象分身 上传记录视频要求参数配置 四、声音克隆 克隆记录参数配置声音要求文案示例 五、AI文案 生成记录创作模型模型分类Al配置 六、充值 充值订单积分套…

seo网站优化服务商html网站怎么做视频教程

高防服务器是一种专门用于防御DDoS&#xff08;分布式拒绝服务&#xff09;攻击的服务器。其中一种常见的DDoS攻击就是CC&#xff08;连续性攻击&#xff09;&#xff0c;它通过向目标服务器发送大量的请求来耗尽服务器资源&#xff0c;使网站无法正常运行。高防服务器采用多种…

php网站开发结构弹窗视频网站

堆叠和IRF其实可以近似看成同一种技术&#xff0c;只是华三叫IRF&#xff0c;华为叫智能堆叠 智能堆叠&#xff08;iStack&#xff09;&#xff1a;支持堆叠特性的交换机通过堆叠线缆连接在一起&#xff0c;从逻辑上变成一台交换设备&#xff0c;作为一个整体参与数据转发&…

网站开发老是弹广告vi手册

本文旨在以初学者的角度来学习Java异常的知识&#xff0c;尽量简单&#xff0c;一些细枝末节的知识不会讲述&#xff0c;但不影响对知识的掌握。&#xff08;比如try-catch可以嵌套&#xff0c;不太会这么用&#xff09;1.什么是异常我们先举个例子int 在IDE里输入这样一个stat…

南宁网站的优化seo整站优化哪家专业

Windows 7 OpenGL配置&#xff0c;解决“无法启动此程序,因为计算机中丢失glut32.dll。”转载于:https://www.cnblogs.com/yangai/p/6253332.html

怎么给网站添加代码只做鱼网站

1.php 不死马权限维持 <?php ignore_user_abort(); //关掉浏览器&#xff0c;PHP脚本也可以继续执行. set_time_limit(0);//通过set_time_limit(0)可以让程序无限制的执行下去 $interval 5; // 每隔*秒运行 do { $filename test.php; if(file_exists($filename)) { echo…

什么叫静态网站seo网络培训

目录 报错信息 解决方法 报错信息 如下所示&#xff08;地址已盲&#xff09;&#xff1a; 控制台报错说找不到这些资源&#xff0c;但是 dist 目录下都有这些 JS 文件啊&#xff0c;这是为什么&#xff1f; 解决方法 在 Vue 项目的 vue.config.js 文件中&#xff0c;添加 …

丝绸之路网站平台建设网站底部 图标

近年来&#xff0c;很多人想要从其他行业跳槽转入计算机领域。非计算机科班如何丝滑转码&#xff1f; 如何规划才能实现转码&#xff1f; 对于非计算机科班的人来说&#xff0c;想要顺利转码成为计算机相关岗位的从业者&#xff0c;需要经过以下几个步骤&#xff1a; 规划转码…

网站建设如何选择宝安网站设计流程

场景还原 今天有小伙伴练习Nginx配置负载均衡时总是无法使用配置好的网址访问 配置文件信详情 http {# 负载均衡 后端IP地址和端口 webservers 策略 轮询upstream webservers{server 192.168.1.100:8080 weight90; server 127.0.0.1:8080 weight10; }server{listen 80;ser…