手写一个Promise

Promise

Promise是一个对象,用于解决异步变成的问题,由传统的异步回调为服务端立即调用优化为使用者者掌握回调主动权。
比如传统的JSONP,如下,在请求路由里添加回调函数,由接收请求的一方来调用请求,使用者只能被动等待获取请求返回数据进行处理

	const callback = (res) => {
		console.log(res);
	}
	const request = (callback) => {
		let script = document.createElement('script');
		script.src = "http://localhost:8080/getInfo?callback=" + callback;
		document.body.appendChild(script);
	}

当开发者通过Promise掌握主动权后,则可以选择回调的时机。
Promise还解决了多请求按顺序执行的回调地狱。使得异步方法可以像同步方法那样获取返回值,Promise执行会返回一个Promise。

思路

先明确Promise的主要几个特点:
1. 三个状态:pending, fullfilled,rejected,且状态一经改变不得回滚
2. 2个回调:成功的回调,错误的回调
3. 回调的传参

基础实现

class Promise {
    constructor(fn) {
        this.state = 'pending'; //先初始化状态
        this.resolveCallbacks = []; //成功的回调
        this.rejectCallbacks = []; //错误的回调
        this.value = null; //初始化返回值
        fn(this.resolve.bind(this), this.reject.bind(this)); //执行函数,并将回调作为入参返回
    }
    then(resolvecb, rejectcb){
    	//请求仍待定,就将回调放进对应内存
        if(this.state === 'pending'){
            this.resolveCallbacks.push(resolvecb);
        	this.rejectCallbacks.push(rejectcb);
        }else if (this.state === 'Fulfilled'){
        	//完成状态执行成功的回调
            resolvecb(this.value);
        }else if(this.state === 'rejected'){
        	//拒绝回调执行拒绝回调
            rejectcb(this.value);
        }
    }
    resolve(value) {
        if(this.state === 'pending'){ //当状态为初始状态时,改变状态,并执行回调
            this.state = 'Fulfilled';
            this.value = value;
            this.resolveCallbacks.map(cb => cb(value));
        }
        
    }
    reject(value) {
        if(this.state === 'pending'){ //当状态为初始状态时,改变状态,并执行回调
            this.state = 'rejected';
            this.value = value;
            this.rejectCallbacks.map(cb => cb(value));
        }
    }
}
let p1 = new Promise((resolve, reject) => {
		reject('hello world');}
	);
let p2 = new Promise((resolve, reject) => {
		resolve('hello world');}
	);
p1.then(res=>{console.log('success', res)});
p2.then(,res=>{console.log('err', res)});

结果如下,可见已经初步实现了一个简易Promise
在这里插入图片描述

链式调用

再思考怎么能让他链式调用呢,也就是.then执行之后要返回一个Promise,可以继续执行.then,回调获取到的value是什么,是上一个请求的值。
那就从then方法入手,加个返回值
先加个this

then(resolvecb, rejectcb){
    	//请求仍待定,就将回调放进对应内存
        if(this.state === 'pending'){
            this.resolveCallbacks.push(resolvecb);
        	this.rejectCallbacks.push(rejectcb);
        }else if (this.state === 'Fulfilled'){
        	//完成状态执行成功的回调
            resolvecb(this.value);
        }else if(this.state === 'rejected'){
        	//拒绝回调执行拒绝回调
            rejectcb(this.value);
        }
        return this; //默认返回当前的Promise
    }
    
   p1.then(res=>{console.log('success', res)}).then(res => {console.log(res);});

在这里插入图片描述
也就是一个请求可以无限次的链式调用并获取到之前的请求返回value值
如果要执行这个呢,也就是在.then里又执行了一个新的Promise,并返回。

let p1 = new Promise((resolve, reject) => {reject('hello world') });
p1.then(
	res => console.log(res), 
	err => {
		console.log(err, 'err'); 
		return new Promice(  //返回一个新的Promise
			(resolve, reject) => 
				{ reject('hello lian') }
			)
		})
	.then(
	(res) => console.log(res), 
	err => console.log(err, 'err')
	);

如果只返回了一个this
结果依然是第一个请求的返回值
在这里插入图片描述

那是不是直接在resolve 和 reject执行回调的时候直接return 回去当前回调执行的返回值就行了

then(resolvecb, rejectcb){
    	//请求仍待定,就将回调放进对应内存
        if(this.state === 'pending'){
            this.resolveCallbacks.push(resolvecb);
        	this.rejectCallbacks.push(rejectcb);
        }else if (this.state === 'Fullfilled'){
        	//完成状态执行成功的回调
            return resolvecb(this.value); //返回回调执行的返回值
        }else if(this.state === 'rejected'){
        	//拒绝回调执行拒绝回调
            return rejectcb(this.value); //返回回调执行的返回值
        }
        return this; //默认返回当前的Promise
    }

在这里插入图片描述
又有个问题,如果我的回调返回的不是一个Promise呢,比如:就返回一个数字

	new Promise((resolve) => resolve('hello')).then(res => {console.log(res); return 3;}).then(res => {console.log(res);})

那是不是可以针对当前返回值进行判断是不是Promise类型,再进行返回。

let res = resolvecb(this.value);
return res?.prototype === 'Promise' ? res : Promise.resolve(res);

其实直接返回Promise.resolve(res)就行了

以上就是基础Promise的实现,仍有未考虑到的缺陷:catch处理等
比如原型上的catch可以

Promise.prototype.catch = function(fn){
   	fn(this.value);
}

比如我们老是直接Promise.resolve

Promise.resolve = function(value){
    return new Promise(resolve=>resolve(value));
}

类推 Promise.reject

Promise.reject= function(value){
    return new Promise(()=>{}, reject=>reject(value));
}

Promise其他方法的实现

all

效果:接收一个Promise数组(iterable类型),等所有的Promise执行完成后,返回一个Promise,且resolve回调里包含所有Promise的执行结果,注意点:一旦Promise被reject或报错,直接返回reject回调。

	Promise.myAll = function(promises){
		let result = []; //使用数组记录结果
	    let count = 0; // 对执行完的promise计数
	    return new Promise((resolve, reject) =>{
	        promises.forEach((item, index)=>{
	            Promise.resolve(item).then(res =>{
	                result[index] = res; //按index记录结果
	                count++;
	                if(result.length === arr.length){
	                    resolve(result);
	                }
	            }, reject) //对于reject的promise直接返回
	        })
	    })
	}

如果 不需要知道结果的话,可以使用reduce让他们按顺序执行

Promise.myAll = function(...arr){
    return arr.reduce((pre,cur)=> pre.then(()=>cur), Promise.resolve());
}

any

效果:接收一个Promise数组(iterable类型),返回最先Fulfilled执行完成的Promise结果,如果没有Fulfilled的Promise,则返回所有rejected的结果集合,跟all是相反的情况

Promise.myAny = function(promises){
    let result = [];
    let count = 0;
    return new Promise((resolve,reject) =>{
        promises.forEach((item, index)=>{
            Promise.resolve(item).then(resolve, res =>{
                result[index] = 'err' + res;
                count++;
                if(count === result.length){
                    reject(new Error('AggregateError: All promises were rejected'));
                }
            })
        })
    })
}

rice

效果:接收一个Promise数组(iterable类型),返回最先改变状态的Promise,也就是不需要判断是否全部执行完,哪个先执行完就返回那个不管结果的状态

Promise.myRice = function(promises){
    return new Promise((resolve,reject) =>{
        promises.forEach(item => Promise.resolve(item).then(resolve,reject));
    })
}

allSettled

效果:接收一个Promise数组(iterable类型),必须等所有Promise改变状态再返回结果集合,相当于all + any

Promise.mySettled = function(promises){
    let result = [];
    let count = 0;
    return new Promise((resolve,reject) =>{
        promises.forEach((item,index) => Promise.resolve(item).then(res =>{
            result[index] = {status: 'fulfilled', value: res};
            count++;
            if(count === promises.length){
                resolve(result);
            }
        },err =>{
            result[index] = {status: 'rejected', value: err};
            count++;
            if(count === promises.length){
                resolve(result);
            }
        }));
    })
}

以下是测试案例

const p1 = Promise.resolve('p1');
const p2 = new Promise((resolve, reject)=> {
  setTimeout(() => {
    resolve('p2 延迟一秒');
  })
}, 1000);
const p3 = new Promise((resolve, reject)=> {
  setTimeout(() => {
    resolve('p3 延迟2秒');
  })
}, 2000);
const p4 = Promise.reject('p4 reject');
const p5 = new Promise((resolve, reject)=> {
  setTimeout(() => {
    reject('p5 失败1.5秒');
  })
}, 1500);

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

kafka笔记

消息队列 场景模式基础架构发送原理异步发送同步发送分区生产者提高吞吐量:数据可靠性ack应答数据重复幂等性事务数据有序数据乱序broker工作流程follower故障leader故障数据查找文件清除高效读写消费者流程消费者组初始化分区分配策略自动提交offset手动提交指定位…

Kubernetes调度器源码学习(一):调度器工作原理、调度器启动流程、调度队列

本文基于Kubernetes v1.22.4版本进行源码学习 1、调度器工作原理 1)、调度流程 kube-scheduler的主要作用就是根据特定的调度算法和调度策略将Pod调度到合适的Node节点上去,是一个独立的二进制程序,启动之后会一直监听API Server&#xff0…

thanos prometheus 的高可用、长期存储二进制部署

1.简介 http://thanos.io/ thanos 是具有长期存储功能的开源、高可用性 Prometheus的集群组件。 全局查询视图 跨多个 Prometheus 服务器和集群查询指标 无限保留 使用对象存储扩展系统,不限时间保留指标。 Prometheus兼容 兼容 Prometheus api,用于…

FPGA时序知识点(基本方法总结就两点:1.降低时钟频率2.减小组合逻辑延迟(针对Setup Slack公式来的)

1.我们说的所有时序分析都是建立在同步电路的基础上的,异步电路不能做时序分析(或者说只能做伪路径约束(在设伪路径之前单bit就打拍,多bit就异步fifo拉到目的时钟域来))。——FPGA 设计中寄存器全部使用一个…

Spring的事务

(1) 事务的定义 事务就是用户定义的一系列数据库操作,这些操作可以视为一个完成的逻辑处理工作单元,要么全部执行,要么全部不执行,是不可分割的工作单元。 (2)事务的使用: begin transaction commit rollback. begin …

谈谈软件系统重构

「头条关注【Java思享汇】,面试、各种技术栈、架构设计持续更新中~」 分享初衷:工作几年之后基本都会经历过大大小小的系统重构,笔者经历过单体应用拆分微服务的系统重构,数据异构,业务系统重构。借助此次…

总结819

学习目标: 4月(复习完高数18讲内容,背诵21篇短文,熟词僻义300词基础词) 第二周: 学习内容: 暴力英语:早上背诵《think different》记150词,默写了两篇文章&#xff0c…

Java中的Iterator底层原理实现

两个抽象方法 Iterator主要有两个抽象方法,让子类实现。 hasNext()用来判断还有没有数据可供访问。next()方法用于访问集合的下一个数据。 这两个方法不像List的get()那样依赖索引获取数据,也不像Queue的poll方法那样依赖特定规则获取数据。 迭代器的…

3月更新 | Visual Studio Code Python

我们很高兴地宣布,2023年3月版 Visual Studio Code Python 和 Jupyter 扩展现已推出! 此版本包括以下改进: 后退按钮和取消功能添加到创建环境命令默认情况下,Python 扩展不再附带 isortJupyter 笔记本中内核选择的改进Python P…

代码随想录Day49

今天继续学习动规解决完全背包问题。 322.零钱兑换 给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。 计算并返回可以凑成总金额所需的最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,…

java 线段树

线段树是一种二叉搜索树,什么叫做二叉搜索树,首先满足二叉树,每个结点度小于等于二,即每个结点最多有两颗子树,何为搜索,我们要知道,线段树的每个结点都存储了一个区间,也可以理解成…

【JavaWeb】8—过滤器

⭐⭐⭐⭐⭐⭐ Github主页👉https://github.com/A-BigTree 笔记链接👉https://github.com/A-BigTree/Code_Learning ⭐⭐⭐⭐⭐⭐ 如果可以,麻烦各位看官顺手点个star~😊 如果文章对你有所帮助,可以点赞👍…

C语言中宏的一些高级用法举例

C语言中宏的一些高级用法 文章目录C语言中宏的一些高级用法1.字符串化2.标记的拼接3.宏的嵌套替换多条语句防止头文件被重复包含宏的可变参数应用方式1方式2方式34.常用宏宏和函数的区别1.字符串化 #include <stdio.h> #include <stdbool.h> #include <string.…

测试开发常问面试题

Postman Postman实现接口关联 步骤 通过正则表达式或则JSON提取器取值的方式&#xff0c;提取需要的参数。将参数设置为全局变量或则环境变量。在之后接口中&#xff0c;通过{{全局变量/环境变量}}代替要替换的参数值。 - JSON提取器方式 var jsonData JSON.parse(respons…

【Spring6】数据校验:Validation

10、数据校验&#xff1a;Validation 10.1、Spring Validation概述 在开发中&#xff0c;我们经常遇到参数校验的需求&#xff0c;比如用户注册的时候&#xff0c;要校验用户名不能为空、用户名长度不超过20个字符、手机号是合法的手机号格式等等。如果使用普通方式&#xff0c…

TenserRT(三)PYTORCH 转 ONNX 详解

第三章&#xff1a;PyTorch 转 ONNX 详解 — mmdeploy 0.12.0 文档 torch.onnx — PyTorch 2.0 documentation torch.onnx.export 细解 计算图导出方法 TorchScript是一种序列化和优化PyTorch模型的格式&#xff0c;将torch.nn.Module模型转换为TorchScript的torch.jit.Scr…

unicloud 模糊查询解决方案

序 1、where和aggregate的模糊搜索 2、第一种是“你好”去匹配“你好啊大家” 3、第二种是“家啊”去匹配“啊&#xff01;你家呢” 只要有1个字匹配就匹配 4、第三种是“家啊”去匹配“啊&#xff01;你家呢” 必须有“家”又有“啊”才匹配” 想看效果&#xff0c;大家可以自…

ROBOGUIDE教程:FANUC机器人摆焊焊接功能介绍与虚拟仿真操作方法

目录 摆焊功能简介 摆焊指令介绍 摆焊功能设置 摆焊条件设置 机器人摆焊示教编程 仿真运行 摆焊功能简介 使用FANCU机器人进行弧焊焊接时&#xff0c;也可以实现摆动焊接&#xff08;简称摆焊&#xff09;。 摆焊功能是在机器人弧焊焊接时&#xff0c;焊枪面对焊接方向…

面试字节,三面HR天坑,想不到自己也会阴沟里翻船....

阎王易见&#xff0c;小鬼难缠。我一直相信这个世界上好人居多&#xff0c;但是也没想到自己也会在阴沟里翻船。我感觉自己被字节跳动的HR坑了。 在这里&#xff0c;我只想告诫大家&#xff0c;offer一定要拿到自己的手里才是真的&#xff0c;口头offer都是不牢靠的&#xff0…

【CE】Mac下的CE教程Tutorial:进阶篇(第8关:多级指针)

▒ 目录 ▒&#x1f6eb; 导读开发环境1️⃣ 第8关&#xff1a;多级指针翻译操作验证其它方案&#x1f6ec; 文章小结&#x1f4d6; 参考资料&#x1f6eb; 导读 开发环境 版本号描述文章日期2023-03-操作系统MacOS Big Sur 11.5Cheat Engine7.4.3 1️⃣ 第8关&#xff1a;多…