JS手写代码篇---手写Promise

4、手写promise

Promise 是一个内置对象,用于处理异步操作。Promise 对象表示一个尚未完成但预期将来会完成的操作。

Promise 的基本结构

一个 Promise 对象通常有以下状态:

  • pending(进行中):初始状态,既不是成功也不是失败。
  • fulfilled(已成功):操作成功完成。
  • rejected(已失败):操作失败。

promise原生代码:

      let promise = new Promise((resolve, reject) => {setTimeout(() => {resolve("下次一定");})});// then的用法promise.then((result) => {console.log(result);},(error) => {console.log(error);}).then((result) => {console.log(result);},(error) => {console.log(error);});

(1).初始结构

使用promise的基本结构:

    let promise = new Promise((resolve, reject) => {resolve("下次一定");});

先创建一个Promise实例,传入一个函数,函数有两个参数,而且promise是有三种状态的,执行reject还是resolve都要根据promise的状态来定

    //    因为promise的创建是:promise = new Promise(() => {})// 所以原生promose我们使用类class Commitment{// prmise有三种状态,全局定义static PENDING = "待定";static FULFILLED = "成功";static REJECTED = "拒绝";// promise一般会传入一个函数且参数为resolve和rejectconstructor(func){// 状态this.status = Commitment.PENDING;// 结果this.result = null;this.error = null;// this调用自身的方法func(this.resolve , this.reject)}// resolveresolve(result){// 判断状态if(this.status === Commitment.PENDING){Commitment.status = Commitment.FULFILLED;this.result = result;}}// rejectreject(error){if(this.status === Commitment.PENDING){Commitment.status = Commitment.REJECTED;this.error = error;}}}

(2).this指向

但是我们测试发现了问题:

promise自己写.html:38 Uncaught TypeError: Cannot read properties of undefined (reading 'status')at reject (promise自己写.html:38:21)at promise自己写.html:49:9at new Commitment (promise自己写.html:24:13)at promise自己写.html:47:21

Cannot read properties of undefined (reading ‘status’):this已经跟丢了

解决class的this指向问题:箭头函数、bind或者proxy

      constructor(func){// 状态this.status = Commitment.PENDING;// 结果this.result = null;this.error = null;// this调用自身的方法,确保这些方法在被调用时,this 的值指向当前的 Commitment 实例func(this.resolve.bind(this) , this.reject.bind(this));}
  • bind(this) 会创建一个新的函数,这个新函数的 this 值被永久绑定到当前 Commitment 实例(即 this 指向当前的 Commitment 对象)。
  • 这样,无论 resolvereject 方法在哪里被调用,this 始终指向 Commitment 实例,确保你可以正确访问实例的属性和方法(如 this.statusthis.result 等)。

(3).then

传入两个参数,一个是成功的回调,一个是失败的回调,但是还要判断条件

  promise.then((result) => {console.log(result);},(error) => {console.log(error);})
        then(onFULFILLED , onREJECTED){// 判断状态if(this.status === Commitment.FULFILLED){// 执行成功的回调onFULFILLED(this.result);}if(this.status === Commitment.REJECTED){// 执行失败的回调onREJECTED(this.error);}}

(4).执行异常

1、执行报错

   // 测试const promise = new Commitment((resolve, reject) => {throw new Error("我报错啦");});

抛出错误,要识别

        // promise一般会传入一个函数且参数为resolve和rejectconstructor(func){// 状态this.status = Commitment.PENDING;// 结果this.result = null;this.error = null;// 在执行之前try...catch捕获错误try{// this调用自身的方法,确保这些方法在被调用时,this 的值指向当前的 Commitment 实例func(this.resolve.bind(this) , this.reject.bind(this));}catch(error){// 捕获错误this.reject(error);}}

2、当传入的then参数不为函数的时候

// 测试const promise = new Commitment((resolve, reject) => {resolve("成功了");});promise.then (undefined,//成功error => { console.log("失败:", error.message); } // 失败回调(可选))

我们会发现它报错了,解决方法就是判断类型

promise自己写.html:57 Uncaught TypeError: onFULFILLED is not a functionat Commitment.then (promise自己写.html:57:17)at promise自己写.html:72:13
        // then方法:传入两个参数,一个是成功的回调,一个是失败的回调// 但是还要判断条件then(onFULFILLED , onREJECTED){// 判断状态以及是否为函数if(this.status === Commitment.FULFILLED && typeof onFULFILLED === 'function'){// 执行成功的回调onFULFILLED(this.result);}if(this.status === Commitment.REJECTED && typeof onREJECTED === 'function'){// 执行失败的回调onREJECTED(this.error);}}

(5)异步

then实现异步–setTimeOut

     // then方法:传入两个参数,一个是成功的回调,一个是失败的回调// 但是还要判断条件then(onFULFILLED , onREJECTED){// 判断状态以及是否为函数if(this.status === Commitment.FULFILLED && typeof onFULFILLED === 'function'){// 执行成功的回调onFULFILLED(this.result);}if(this.status === Commitment.REJECTED && typeof onREJECTED === 'function'){// 执行失败的回调onREJECTED(this.error);}}

resolve是异步的,then也是调用的

原生的promise:

 console.log("第一步");let promise = new Promise((resolve, reject) => {console.log("第二步");setTimeout(() => {resolve("下次一定");console.log("第四步");})});// then的用法promise.then((result) => {console.log(result);},(error) => {console.log(error);})console.log("第三步");
第一步
第二步
第三步
第四步
下次一定

但是手写promise

   console.log("第一步");const promise = new Commitment((resolve, reject) => {console.log("第二步");setTimeout(() => {  //执行thenresolve("成功了");console.log("第四步");}, 0);});promise.then (result => {console.log("成功"  , result.message); },//成功error => { console.log("失败:", error.message); } // 失败回调(可选))console.log("第三步");
第一步
第二步
第三步
第四步

原因:then中状态判断的问题,settimeout之后它就执行then方法了,但是此时的状态还是待定,但是then里面并没有处理待定状态

(6)回调保存

解决:判定待定状态,保留then里面的函数,所以我们要用数组保存函数

但是我们发现还是有问题resolve和reject是在事件循环末尾执行的,他们也要添加resolve和reject

<script>//    因为promise的创建是:promise = new Promise(() => {})// 所以原生promose我们使用类class Commitment {// prmise有三种状态,全局定义static PENDING = "待定";static FULFILLED = "成功";static REJECTED = "拒绝";// promise一般会传入一个函数且参数为resolve和rejectconstructor(func) {// 状态this.status = Commitment.PENDING;// 结果this.result = null;this.error = null;//   数组:用于保存成功和失败的回调this.onFulfilledCallbacks = [];//新增this.onRejectedCallbacks = [];//新增// 在执行之前try...catch捕获错误try {// this调用自身的方法,确保这些方法在被调用时,this 的值指向当前的 Commitment 实例func(this.resolve.bind(this), this.reject.bind(this));} catch (error) {// 捕获错误this.reject(error);}}// resolveresolve(result) {setTimeout(() => {// 判断状态if (this.status === Commitment.PENDING) {this.status = Commitment.FULFILLED;this.result = result;// 执行成功的回调--新增this.onFulfilledCallbacks.forEach((callback) => {callback(result);});}});}// rejectreject(error) {setTimeout(() => {if (this.status === Commitment.PENDING) {this.status = Commitment.REJECTED;this.error = error;// 执行失败的回调--新增this.onRejectedCallbacks.forEach((callback) => {callback(error);});}});}// then方法:传入两个参数,一个是成功的回调,一个是失败的回调// 但是还要判断条件then(onFULFILLED, onREJECTED) {// 判断状态以及是否为函数//   如果是待定,就要保存函数到数组里面--新增if (this.status === Commitment.PENDING) {this.onFulfilledCallbacks.push(onFULFILLED);this.onRejectedCallbacks.push(onREJECTED);}if (this.status === Commitment.FULFILLED &&typeof onFULFILLED === "function") {// then是异步的setTimeout(() => {// 执行成功的回调onFULFILLED(this.result);});}if (this.status === Commitment.REJECTED &&typeof onREJECTED === "function") {// then是异步的setTimeout(() => {// 执行失败的回调onREJECTED(this.error);});}}}// 测试console.log("第一步");const promise = new Commitment((resolve, reject) => {console.log("第二步");setTimeout(() => {resolve("成功了");console.log("第四步");}, 0);});promise.then((result) => {console.log(result);}, //成功(error) => {console.log(error);} // 失败回调(可选));console.log("第三步");</script>

(7)链式

promise的链式是新建一个promise对象,我们直接返回this就好了

 then(onFULFILLED, onREJECTED) {// 判断状态以及是否为函数//   如果是待定,就要保存函数到数组里面if (this.status === Commitment.PENDING) {this.onFulfilledCallbacks.push(onFULFILLED);this.onRejectedCallbacks.push(onREJECTED);}if (this.status === Commitment.FULFILLED &&typeof onFULFILLED === "function") {// then是异步的setTimeout(() => {// 执行成功的回调onFULFILLED(this.result);});}if (this.status === Commitment.REJECTED &&typeof onREJECTED === "function") {// then是异步的setTimeout(() => {// 执行失败的回调onREJECTED(this.error);});}return this; // 返回当前的promise对象}}

为什么直接返回this可以实现回调呢???

方法调用和返回值

在 JavaScript 中,方法的调用(例如 obj.method())会返回方法的返回值。

如果方法返回 this(即对象本身),那么调用该方法后,你仍然持有对该对象的引用。

链式调用的实现

当你调用

obj.method1().method2()

时:

obj.method1()被调用,并返回obj(因为 method1返回this)。

然后 method2()被调用,其调用者仍然是obj`。

这样,你可以连续调用多个方法,形成链式调用。

**总结:**手写 Promise 的核心在于实现状态管理、异步回调和链式调用。首先,Promise 有三种状态(pending/fulfilled/rejected),通过构造函数接收执行器函数,并用 resolvereject 更新状态。

关键点包括:**状态管理:**初始为 pending,调用 resolvereject 后不可逆地变为 fulfilledrejected异步回调:then 方法需异步执行回调(用 setTimeout),若状态为 pending,需将回调存入数组,待状态变更后遍历执行。**错误捕获:**构造函数中用 try/catch 捕获执行器函数的同步错误,并调用 reject链式调用:then 返回 this,使后续 then 能继续绑定回调,形成链式调用。

最终实现需确保:

  • 状态变更后异步触发回调。
  • 回调参数类型检查(非函数则忽略)。
    象的引用。

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

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

相关文章

我喜欢的vscode几个插件和主题

主题 Monokaione Monokai Python 语义高光支持 自定义颜色为 self 将 class , def 颜色更改为红色 为装饰器修复奇怪的颜色 适用于魔法功能的椂光 Python One Dark 这个主题只在python中效果最好。 我为我个人使用做了这个主题,但任何人都可以使用它。 插件 1.Pylance Pylanc…

【深度学习新浪潮】大模型时代,我们还需要学习传统机器学习么?

在大模型时代,AI 工程师仍需掌握传统机器学习知识,这不仅是技术互补的需求,更是应对复杂场景和职业发展的关键。以下从必要性和学习路径两方面展开分析: 一、传统机器学习在大模型时代的必要性 技术互补性 大模型(如GPT、BERT)擅长处理复杂语义和生成任务,但在数据量少…

年度工作计划总结述职报告PPT模版一组分享

工作计划总结述职报告PPT模版&#xff1a;工作计划述职报告PPT模版https://pan.quark.cn/s/fba40a5e87da 第一套PPT模版是医院年度工作计划的封面页&#xff0c;有蓝橙配色、医院标题、年度工作计划的大字、英文副标题、汇报人信息和右上角的医院logo区域&#xff0c;右侧还有医…

软件设计师“排序算法”真题考点分析——求三连

一、考点分值占比与趋势分析 综合知识题分值统计表 年份考题数量总分值分值占比考察重点2018222.67%时间复杂度/稳定性判断2019334.00%算法特性对比分析2020222.67%空间复杂度要求2021111.33%算法稳定性判断2022334.00%综合特性应用2023222.67%时间复杂度计算2024222.67%分治…

华为云Flexus+DeepSeek征文|基于华为云Flexus云服务的云服务器单机部署Dify-LLM应用开发平台

目录 一、前言 二、华为云Flexus云服务优势 三、华为云Flexus一键部署Dify 3.1 选择模板 3.2 参数配置 3.3 资源栈设置 3.4 配置确认 3.5 创建执行计划 3.6 部署 四、Dify-LLM应用开发平台初体验 4.1 访问Dify-LLM应用开发平台 4.2 设置管理员账户 4.3 登录Dify-LLM应用开发平台…

智能指针RAII

引入&#xff1a;智能指针的意义是什么&#xff1f; RAll是一种利用对象生命周期来控制程序资源&#xff08;如内存、文件句柄、网络连接、互斥量等等&#xff09;的简单技术。 在对象构造时获取资源&#xff0c;接着控制对资源的访问使之在对象的生命周期内始终保持有效&#…

nt!MiRemovePageByColor函数分析之脱链和刷新颜色表

第0部分&#xff1a;背景 PFN_NUMBER FASTCALL MiRemoveZeroPage ( IN ULONG Color ) { ASSERT (Color < MmSecondaryColors); Page FreePagesByColor[Color].Flink; if (Page ! MM_EMPTY_LIST) { // // Remove the first entry on the zeroe…

DEBUG:Lombok 失效

DEBUG&#xff1a;Lombok 失效 问题描述 基于 Spring Boot 的项目中&#xff0c;编译时显示找不到 log 属性。查看对应的 class 类&#xff0c;Lombok 正常在编译时生成 log 属性。 同时存在另一个问题&#xff0c;使用Getter注解&#xff0c;但实际使用中该注解并没有生效&…

3D几何建模引擎3D ACIS Modeler核心功能深度解读

3D ACIS Modeler是一款由Spatial Corporation&#xff08;现为Dassault Systmes旗下&#xff09;开发的工业级三维几何建模内核&#xff0c;为CAD/CAM/CAE、建筑、制造、测量及三维动画等领域提供底层建模能力。本文将从基本定位、核心功能及行业案例三方面&#xff0c;系统介绍…

Flutter - 集成三方库:数据库(sqflite)

数据库 $ flutter pub add sqlite $ flutter pub get$ flutter run运行失败&#xff0c;看是编译报错,打开Xcode工程 ⌘ B 编译 对比 GSYGithubAppFlutter 的Xcode工程Build Phases > [CP] Embed Pods Frameworks 有sqfite.framework。本地默认的Flutter工程默认未生成Pod…

Android 中 权限分类及申请方式

在 Android 中,权限被分为几个不同的类别,每个类别有不同的申请和管理方式。 一、 普通权限(Normal Permissions) 普通权限通常不会对用户隐私或设备安全造成太大风险。这些权限在应用安装时自动授予,无需用户在运行时手动授权。 android.permission.INTERNETandroid.pe…

目标检测指标计算

mAP&#xff08;mean Average Precision&#xff09; 概述 预备参数&#xff1a;类别数&#xff0c;IoU阈值&#xff0c;maxDets值&#xff08;每张测试图像最多保留maxDets个预测框&#xff0c;通常是根据置信度得分排序后取前maxDets个&#xff09;&#xff1b; Q: 假如某张…

联合索引失效情况分析

一.模拟表结构&#xff1a; 背景&#xff1a; MySQL版本——8.0.37 表结构DDL&#xff1a; CREATE TABLE unite_index_table (id bigint NOT NULL AUTO_INCREMENT COMMENT 主键,clomn_first varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMEN…

软件架构之-论分布式架构设计及其实现

论分布式架构设计及其实现 摘要正文摘要 2023年2月,本人所在集团公司承接了长三角地区某省渔船图纸电子化审查项目开发,该项目旨在为长三角地区渔船建造设计院、渔船审图机构提供一个便捷化的服务平台。在次项目中,我作为项目成员参与了整个项目的建设工作,全权负责项目需求…

Pydantic数据验证实战指南:让Python应用更健壮与智能

导读&#xff1a;在日益复杂的数据驱动开发环境中&#xff0c;如何高效、安全地处理和验证数据成为每位Python开发者面临的关键挑战。本文全面解析了Pydantic这一革命性数据验证库&#xff0c;展示了它如何通过声明式API和类型提示系统&#xff0c;彻底改变Python数据处理模式。…

3、ubantu系统 | 通过vscode远程安装并配置anaconda

1、vscode登录 登录后通过pwd可以发现目前位于wangqinag账号下&#xff0c;左侧为属于该账号的文件夹及文件。 通过cd ..可以回到上一级目录&#xff0c;通过ls可以查看当前目录下的文件夹及文件。 2、安装 2.1、下载anaconda 通过wget和curl下载未成功&#xff0c;使用手动…

Python 与 Java 在 Web 开发中的深度对比:从语言特性到生态选型

在 Web 开发领域&#xff0c;Python 和 Java 作为两大主流技术栈&#xff0c;始终是开发者技术选型时的核心考量。本文将从语言本质、框架生态、性能工程、工程实践等多个维度展开深度对比&#xff0c;结合具体技术场景解析两者的适用边界与融合方案&#xff0c;为开发者提供系…

【OpenGL学习】(一)创建窗口

文章目录 【OpenGL学习】&#xff08;一&#xff09;创建窗口 【OpenGL学习】&#xff08;一&#xff09;创建窗口 GLFW OpenGL 本身只是一套图形渲染 API&#xff0c;不提供窗口创建、上下文管理或输入处理的功能。 GLFW 是一个支持创建窗口、处理键盘鼠标输入和管理 OpenGL…

电脑闪屏可能的原因

1. 显示器 / 屏幕故障 屏幕排线接触不良&#xff1a;笔记本电脑屏幕排线&#xff08;屏线&#xff09;松动或磨损&#xff0c;导致信号传输不稳定&#xff0c;常见于频繁开合屏幕的设备。屏幕面板损坏&#xff1a;液晶屏内部灯管老化、背光模块故障或面板本身损坏&#xff0c;…

docker容器知识

一、docker与docker compose区别&#xff1a; 1、docker是创建和管理单个容器的工具&#xff0c;适合简单的应用或服务&#xff1b; 2、docker compose是管理多容器应用的工具&#xff0c;适合复杂的、多服务的应用程序&#xff1b; 3、docker与docker compose对比&#xff…