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

Promise原理、以及Promise.race、Promise.all、Promise.resolve、Promise.reject实现;

为了向那道光亮奔过去,他敢往深渊里跳;

于是今天朝着Promise的实现前进吧,写了四个小时,终于完结撒花;
我知道大家没有耐心,当然我也坐的腰疼,直接上代码,跟着我的注释一行行看过去,保证门清

const PENDING = "pending"
const FULFILLED = "fulfilled"
const REJECTED = "rejected"
// 提前封装好一个判断promise的 这里不用instanceof 因为有可能在我们的then的参数中,有可能是别人或者自己的promise
const isPromise = value => {return (!!value &&(typeof value === "object" || typeof value === "function") &&typeof value.then === "function")
}
class MyPromise {#state = PENDING // 当前执行状态#result = undefined // 当前结果值#handler = [] // 记录成功与失败回调的数组constructor(executor) {const resolve = value => {// resolve之后去改变当前状态为成功 与当前成功的值this.#changeState(value, FULFILLED)}const reject = reason => {// reject之后去改变当前状态为失败 与当前错误的值this.#changeState(reason, REJECTED)}try {// 这里直接执行参数中的函数executor(resolve, reject)} catch (error) {console.error(error)// 这里try catch 错误就走rejectreject(error)}}// 将传入的函数放到微队列中去执行#runMicroTask(runTask) {// 如果不兼容promiseif (typeof Promise === "function") {return Promise.resolve().then(runTask)}// MutationObserver兼容性更好if (typeof MutationObserver === "function") {const ob = new MutationObserver(runTask)const node = document.createTextNode("")ob.observe(node, { characterData: true })node.data = 1return}// 如果是node环境if (process.nextTick && typeof process.nextTick === "function") {process.nextTick(runTask)}} // 改变状态 保存此次的值 并且执行回调#changeState(result, state) {if (this.#state != PENDING) returnthis.#state = statethis.#result = resultthis.#run()}// 这里跑每次then后的回调#runOne(callback, resolve, reject) {// 这里主要是为了模拟微任务 都是伪代码this.#runMicroTask(() => {// 如果为函数if (typeof callback === "function") {try {// 拿到函数的返回值const data = callback(this.#result)// 如果是promise包括别人封装的promiseif (isPromise(data)) {console.log("data", data)// 如果是promise那就直接去执行.then 函数// 这里需要注意 这个resolve 是给下一个 所以这里这个resolve函数里带有上一次then返回来的值的!/* 可以看这里的注释来理解p.then(testMap.promiseTask, err => {console.log("第一次err err", err)throw "不好意思"}).then(res => {return new MyPromise(mySuc => {console.log("第二次", res)mySuc("MyPromise的值" + res)})},err => {console.log("第二次err err", err)})*/data.then(resolve, reject)} else {// 否则就自行resolve 把then(suc=>return '结果值') 就把这个data结果值给下一次调用的then传递过去resolve(data)}} catch (error) {// 不用解释了吧console.error(error)reject(error)}} else {// 如果不是函数 就直接执行resolveconst settled = this.#state === FULFILLED ? resolve : rejectsettled(this.#result)}})}#run() {if (this.#state === PENDING) return/*
把下面的注释拿上来 主要为了能一次看懂不来回跳动
这里做push主要是
const p = new MyPromise之后
p.then(res=>fn1(res))
p.then(res=>fn2(res))
记录两次 fn1 与fn2
当然如果不是链式调用其实两次的拿到的回调值都是一样的
*/while (this.#handler.length) {// 这里需要注意的是resolve 与reject用的是实际then函数中传递的resolve与reject 不是当前类中的resolveconst { onFulfilled, onRejected, resolve, reject } =this.#handler.shift()//  这里主要是为了简化代码 传递了此次this.#runOne(this.#state === FULFILLED ? onFulfilled : onRejected,resolve,reject)}}then(onFulfilled, onRejected) {return new MyPromise((resolve, reject) => {/*
这里做push主要是
const p = new MyPromise之后
p.then(res=>fn1(res))
p.then(res=>fn2(res))
记录两次 fn1 与fn2
当然如果不是链式调用其实两次的拿到的回调值都是一样的
*/this.#handler.push({onFulfilled,onRejected,resolve,reject,})this.#run()})}//catch方法的封装 catch 的话直接让它执行错误代码就好了 // 可不要以为有这样的代码比如Promise.catch 没有哈 都是new Promise的回调函数reject执行后的,所以这里只要让它有这个 reject方法就行了!catch(onRejected) {return this.then(undefined, onRejected)}//resolve方法的封装,凡是被static修饰的属性和方法都是静态方法和属性,只能被类名调用//不能被实例化对象调用.同时也不能被子类继承,换句话说它属于当前这个类的.static resolve(value) {//返回结果为Promise对象// 这里呢需要判断他是不是promise resolve中可能是个promise return new MyPromise((resolve, reject) => {if (isPromise(value)) {value.then(v => {resolve(v)},r => {reject(r)})} else {resolve(value)}})}//reject方法的封装 reject都是出错这种明确值,所以这里不需要判断 你给啥,我给下一个error给啥static reject(value) {return new MyPromise((resolve, reject) => {reject(value)})}//all方法的封装static all(promises) {const self = thisreturn new MyPromise((resolve, reject) => {let length = promises.length // 缓存一下有多少个promiselet count = 0 // 用于记录resolve的数量let values = new Array(length) // 用于存储resolve返回的值for (let i = 0; i < length; i++) {let promise = promises[i]// 判断数组的每一项,如果是promise,就进入then,不是就直接放进values数组中返回if (isPromise(promise)) {promise.then(res => {// 记录promise完成的数量count++// values存储每一个promise的resvalues[i] = res// 由于异步代码在最后执行,我们需要在then里面判断promise的完成数量,全部完成就resolve// 在for外面判断,是防止它全部都不是promise实例if (count === length) {resolve(values)}}).catch(err => {// 当有一个promise实例reject,我们就直接rejectreject(err)})} else {// 针对不是promise实例count++values[i] = promise}}// 当数据的所有项都不是promise实例,我们就在这判断多一次,然后resolveif (count === length) {resolve(values)}})}//race方法的封装static race(promises) {return new MyPromise((resolve, reject) => {const len = promises.lengthfor (let i = 0; i < len; i += 1) {const promise = promises[i]// 只要有一条成功则全部成功promise.then(res => {resolve(res)},error => {resolve(error)})}})}
}
window.MyPromise = MyPromiseconst test1 = new MyPromise(suc => {setTimeout(() => suc("成功 p"), 1001)
})
const test2 = new MyPromise((resolve, reject) => {setTimeout(() => {reject(3)}, 1000)
})
const test3 = new MyPromise(suc => {setTimeout(() => suc("成功 test3"), 1019)
})
MyPromise.all([test2, test1, test3]).then(res => {console.log("res all", res)}).catch(res => {console.log("error", res)})
MyPromise.race([test2, test1, test3]).then(res => {console.log("res race", res)}).catch(res => {console.log("error", res)})const catchTest = new MyPromise((resolve, reject) => {setTimeout(() => {reject("catch 测试")}, 1000)
}).catch(error => {console.error(error)
})const p = new MyPromise((resolve, reject) => {setTimeout(() => {resolve(3)}, 1000)
})const testMap = {promiseTask: res => {console.log("第一次 ===>", res)return new Promise(suc => {suc("promise方式的res", res)})},funcTask: res => {console.log("第一次 ===>", res)return res},// 直接穿透第一次resolve 或者reject 值到下一条then函数中otherTask: "其他",
}p.then(testMap.promiseTask, err => {console.log("第一次err err", err)throw "不好意思"
}).then(res => {return new MyPromise(mySuc => {console.log("第二次", res)mySuc("MyPromise的值" + res)})},err => {console.log("第二次err err", err)}).then(res => {console.log("第三次", res)return "第三次的值" + res},err => {console.log("第三次err err", err)}).then(res => {console.log("第四次", res)},err => {console.log("第四次err err", err)})

相关文章:

Promise原理、以及Promise.race、Promise.all、Promise.resolve、Promise.reject实现;

为了向那道光亮奔过去&#xff0c;他敢往深渊里跳&#xff1b; 于是今天朝着Promise的实现前进吧&#xff0c;写了四个小时&#xff0c;终于完结撒花&#xff1b; 我知道大家没有耐心&#xff0c;当然我也坐的腰疼&#xff0c;直接上代码&#xff0c;跟着我的注释一行行看过去…...

mysql---MHA(高可用)

MHA概述 magterhight availabulity :基于主库的高可用环境下&#xff0c;主故障切换基础要求&#xff1a;主从架构 &#xff08;一主两从&#xff09;解决mysql的单点故障问题&#xff0c;一旦数据库崩溃&#xff0c;MHA会在0-30s内这东东完成故障切换。复制方式&#xff1a;半…...

人工智能基础_机器学习032_多项式回归升维_原理理解---人工智能工作笔记0072

现在开始我们来看多项式回归,首先理解多维 原来我们学习的使用线性回归,其实就是一条直线对吧,那个是一维的,我们之前学的全部都是一维的对吧,是一维的,然后是多远的,因为有多个x1,x2,x3,x4... 但是比如我们有一个数据集,是上面这种,的如果用一条直线很难拟合,那么 这个时候,…...

C#截取范围

string[] strs new string[]{"1e2qe","23123e21","3ewqewq","4fewfew","5fsdfds"};var list strs[1..2];Range p 0..3;var list strs[Range];...

用 winget 在 Windows 上安装 kubectl

目录 kubectl 是什么&#xff1f; 安装 kubectl 以管理员身份打开 PowerShell 使用 winget 安装 kubectl 测试一下&#xff0c;确保安装的是最新版本 导航到你的 home 目录&#xff1a; 验证 kubectl 配置 kubectl 是什么&#xff1f; kubectl 是 Kubernetes 的命令行工…...

1 Supervised Machine Learning Regression and Classification

文章目录 Week1OverViewSupervised LearningUnsupervised LearningLinear Regression ModelCost functionGradient Descent Week2Muliple FeatureVectorizationGradient Descent for Multiple RegressionFeature ScalingGradient DescentFeature EngineeringPolynomial Regress…...

Antv/G2 折线图 DataSet 数据展开成指定格式

DataSet 文档 G2 3.2 DataSet 文档 Demo&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><m…...

物理问题中常见的分析问题----什么样的函数性质较好

物理问题中常见的积分符号位置交换问题 重极限与累次极限 高数下的定义 累次极限&#xff1a;求极限时需要遵循一定的顺序重极限&#xff1a;任意方向趋于的极限 两者之间的关系&#xff1a; 两者没啥关系存在累次极限存在而不相等的函数...... 求和符号与积分符号互换--逐项积…...

8 Go的函数

概述 在上一节的内容中&#xff0c;我们介绍了Go的指针&#xff0c;包括&#xff1a;使用指针、空指针、指针数组、指向指针的指针等。在本节中&#xff0c;我们将介绍Go的函数。函数允许开发者将相关的代码组织在一起&#xff0c;并将其命名&#xff0c;以便在其他地方进行调用…...

算法笔记-第九章-二叉树的遍历(待整理)

算法笔记-第九章-二叉树的遍历 二叉树的先序遍历二叉树的中序遍历二叉树的先序遍历 //二叉树的先序遍历 #include <cstdio> #include <vector> using namespace std;const int MAXN = 50;struct Node //用结构体表示左子树和右子树的数据 {int l, r; } nodes[MAXN]…...

C语言从入门到精通之【字符串】

C语言没有专门用于储存字符串的变量类型&#xff0c;字符串都被储存在char类型的数组中。数组由连续的存储单元组成&#xff0c;字符串中的字符被储存在相邻的存储单元中&#xff0c;每个单元储存一个字符&#xff0c;每个字符占1个字节。 数组末尾位置的字符\0。这是空字符&am…...

超详细!必看!!STM32--时钟树原理

一、什么是时钟&#xff1f; 时钟是单片机的脉搏&#xff0c;是系统工作的同步节拍。单片机上至CPU&#xff0c;下至总线外设&#xff0c;它们工作时序的配合&#xff0c;都需要一个同步的时钟信号来统一指挥。时钟信号是周期性的脉冲信号。 二、什么是时钟树&#xff1f; S…...

用 Golang 采集 Nginx 接口流量大小

简介 在开发和运维中&#xff0c;我们经常需要监控和分析服务器的接口流量大小&#xff0c;特别是对于部署了 Nginx 的服务器。本文将介绍如何使用 Golang 采集 Nginx 接口流量大小&#xff0c;并展示如何将这些数据进行实时监控和分析。 步骤一&#xff1a;准备工作 在开始…...

Linux java jar启停脚本(合并版)

#包文件路径及名称(目录按照各自配置) APP_NAME=/opt/whkc/gs/app-java.jar#查询进程,并杀掉当前jar/java程序 pid=`ps -ef|grep app-java.jar | grep -v grep | awk {print $2}` kill...

计算机毕业设计选题推荐-公共浴池微信小程序/安卓APP-项目实战

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…...

sqli-labs关卡13(基于post提交的单引号加括号的报错盲注)通关思路

文章目录 前言一、回顾第十二关知识点二、靶场第十三关通关思路1、判断注入点2、爆显位3、爆数据库名4、爆数据库表5、爆数据库列6、爆数据库关键信息 总结 前言 此文章只用于学习和反思巩固sql注入知识&#xff0c;禁止用于做非法攻击。注意靶场是可以练习的平台&#xff0c;…...

SparkAi创作系统ChatGPT网站源码+详细搭建部署教程+AI绘画系统+支持GPT4.0+Midjourney绘画

一、AI创作系统 SparkAi创作系统是基于OpenAI很火的ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统&#xff0c;支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如…...

shiro默认session设置永不超时

Shiro默认情况下session是有超时时间的&#xff0c;而不是永不超时。默认的超时时间是30分钟&#xff0c;可以通过修改Shiro的配置文件来更改超时时间。如果想要让session永不超时&#xff0c;可以将超时时间设置为一个很大的值&#xff0c;例如Integer.MAX_VALUE。 以下是修改…...

前端食堂技术周刊第 104 期:Angular v17、GPTs、Vue vapor mode、Svelte Flow、Bundler 的设计取舍

美味值&#xff1a;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f;&#x1f31f; 口味&#xff1a;金奖乳鸽 食堂技术周刊仓库地址&#xff1a;https://github.com/Geekhyt/weekly 大家好&#xff0c;我是童欧巴。欢迎来到前端食堂技术周刊&#xff0c;我们先来看下…...

list复制出新的list后修改元素,也更改了旧的list?

例子 addAll() Testpublic void CopyListTest(){Student student Student.builder().id(1).name("张三").age(23).classId(1).build();Student student2 Student.builder().id(2).name("李四").age(22).classId(1).build();List<Student> student…...

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...

Docker 离线安装指南

参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性&#xff0c;不同版本的Docker对内核版本有不同要求。例如&#xff0c;Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本&#xff0c;Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...

工业安全零事故的智能守护者:一体化AI智能安防平台

前言&#xff1a; 通过AI视觉技术&#xff0c;为船厂提供全面的安全监控解决方案&#xff0c;涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面&#xff0c;能够实现对应负责人反馈机制&#xff0c;并最终实现数据的统计报表。提升船厂…...

遍历 Map 类型集合的方法汇总

1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...

postgresql|数据库|只读用户的创建和删除(备忘)

CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序

一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...

C# SqlSugar:依赖注入与仓储模式实践

C# SqlSugar&#xff1a;依赖注入与仓储模式实践 在 C# 的应用开发中&#xff0c;数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护&#xff0c;许多开发者会选择成熟的 ORM&#xff08;对象关系映射&#xff09;框架&#xff0c;SqlSugar 就是其中备受…...

大数据学习(132)-HIve数据分析

​​​​&#x1f34b;&#x1f34b;大数据学习&#x1f34b;&#x1f34b; &#x1f525;系列专栏&#xff1a; &#x1f451;哲学语录: 用力所能及&#xff0c;改变世界。 &#x1f496;如果觉得博主的文章还不错的话&#xff0c;请点赞&#x1f44d;收藏⭐️留言&#x1f4…...

JVM虚拟机:内存结构、垃圾回收、性能优化

1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...

【分享】推荐一些办公小工具

1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由&#xff1a;大部分的转换软件需要收费&#xff0c;要么功能不齐全&#xff0c;而开会员又用不了几次浪费钱&#xff0c;借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...