手写一个Promise

news2025/6/21 10:44:26

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.coloradmin.cn/o/411374.html

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

相关文章

小白学Pytorch系列--Torch.nn API Vision Layers(15)

小白学Pytorch系列–Torch.nn API Vision Layers(15) 方法注释nn.PixelShuffle将形状张量(∗,Cr2,H,W)(*,C r^2,H,W)(∗,Cr2,H,W)中的元素重新排列为形状张量(∗,C,Hr,Wr)(*,C,H r,W r)(∗,C,Hr,Wr)&#x…

详细介绍React路由

路由 单页Web应用(single page web application,SPA),整个应用只有一个完整的页面,点击页面中的链接不会刷新页面,只会做页面的局部更新。数据都需要通过ajax请求获取, 并在前端异步展现。 一个路由就是一…

高通SDX12:ProSLIC Si32185移植调试

一、SLIC业务流程图 本次在高通SDX12平台上支持语音芯片Si32185大致流程如下: 驱动部分直接放在Kernel中,通过SPI注册设备创建字符节点,与硬件建立连接注册设备成功并在audio_kernel中正确配置FE、BE后,声卡会创建出来应用层直接放在apps_proc下,通过IOCTL操作驱动层,通…

操作系统、输入法和编码的理解

操作系统和输入法 操作系统有一个输入法管理器,叫做IMM,管理系统内的所有输入法,或者说一个输入法想要使用操作系统提供的输入法API,就必须在IMM进行注册。 应用程序和输入法 应用程序一般都会使用输入法,也就是调用输入法的api进行文字输…

Spring boot+Vue3博客平台:文章搜索与推荐和文章阅读统计模块

一、文章搜索与推荐功能 1.前端搜索功能实现 在文章列表组件中添加搜索框&#xff0c;用户可以输入关键字进行文章搜索。同时&#xff0c;在搜索结果下方展示相关文章推荐。 <template><div class"article-list"><div class"search">&…

手写vuex4源码(六)命名空间实现

一、命名空间使用 在子模块对象中添加 namespaced&#xff1a;true&#xff0c;为模块开启命名空间功能&#xff1b; 开启命名空间功能&#xff0c;相当于为每个模块添加独立的作用域&#xff0c;实现模块间状态和事件的隔离&#xff1b; 二、命名空间实现逻辑 在模块注册阶…

Vue3+Typescript+Vitest单元测试环境+组件测试基础篇

上一章我们把环境配置好了&#xff0c;并且进行了最简单的一个单元测试Vue3TypescriptVitest单元测试环境基础用例篇 现在让我们去编写一个最简单的组件 这个代码包含最简单的部分&#xff0c;就是一个按钮&#xff0c;接受一个内容插槽、自身有一个button样式 了解相关API…

JavaWeb——File类和InputStream,OutputStream类详解

目录 一、File类 1、定义 2、属性 3、构造方法 4、方法 &#xff08;1&#xff09;、获取文件路径 &#xff08;2&#xff09;、文件的创建和删除 &#xff08;3&#xff09;、目录的创建 二、InputStream和OutputStream 1、InputStream &#xff08;1&#xff09;、…

从头创建一个新的浏览器,这合理吗?

从头构建一个新浏览器&#xff1f;这如果是不是个天大的“伪需求”&#xff0c;便是一场开发者的噩梦&#xff01; 要知道&#xff0c;如果没有上百亿的资金和数百名研发工程师的投入&#xff0c;从头开始构建一个新的浏览器引擎&#xff0c;几乎是不可能的。然而SerenityOS系统…

AI风暴 :文心一言 VS GPT-4

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; 文心一言 VS GPT-4 文心一言&#xff1a;知识增强大语言模型百度全新一代知识增强大语言模型&#xff0c;文心大模型家族的新成员&#xff0c;能够与人对话互动&#…

代码随想录Day49

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

虹科分享 | 使用IOTA检查受3CX DLL旁加载攻击影响的客户端 | 网络性能监控

2023年3月底&#xff0c;VoIP制造商3CX&#xff08;流行的互联网语音协议&#xff08;VoIP&#xff09;专用交换机&#xff08;PBX&#xff09;电话系统的开发商&#xff09;遭到DLL旁加载攻击。他们的软件被大约60万家公司和1200万用户使用&#xff0c;其中包括梅赛德斯-奔驰、…

人工智能中的监督学习到底是啥?其应用方向有哪些?

人工智能&#xff08;Artificial Intelligence, AI&#xff09;是一门致力于使机器能够像人类一样进行智能决策和行为的学科。监督学习&#xff08;Supervised Learning&#xff09;是人工智能领域中的一种重要学习方式&#xff0c;通过使用标注好的样本数据来训练模型&#xf…

零基础如何入门网络安全?【2023最新】

前言 最近收到不少关注朋友的私信和留言&#xff0c;大多数都是零基础小友入门网络安全&#xff0c;需要相关资源学习。其实看过的铁粉都知道&#xff0c;之前的文里是有过推荐过的。新来的小友可能不太清楚&#xff0c;这里就系统地叙述一遍。 01.简单了解一下网络安全 说白…

Linux 操作系统原理 — 内核协议栈收包/发包流程

目录 文章目录目录关键技术NIC DMAKernel 中的 sk_buff 与 sk_bufferKernel 中的 Rx/Tx RingBuffer Descriptor Table中断收包机制NAPI 收包机制多队列网卡内核协议栈收包/发包流程概览内核协议栈收包流程详解驱动程序层&#xff08;数据链路层&#xff09;VLAN 协议族Linux Br…

基于MATLAB的语音识别仿真系统

本文实现的语音识别系统&#xff0c;主要是对语音识别的特征参数的提取和识别模型的匹配&#xff0c;进行深入的研究。首先,对语音识别进行了概述&#xff0c;给出了语音识别的系统框架。然后就是如何实现语音识别的问题&#xff0c;这个过程可分为两个部分:第一个是语音特征参…

【Ruby 2D】【unity learn】控制敌人随机运动以及动画控制

前两天考完蓝桥杯稍微休息了一下&#xff0c;昨天做了一个动画控制&#xff0c;但是想到写出来可能会字很多&#xff0c;我就搁置到今天来写了&#xff0c;unity learn是一个官方教程平台&#xff0c;里面有unity assert store的配套教程&#xff0c;全是文档&#xff0c;比看视…

#java mavn安装图像验证码jar失败kaptcha-2.3.2.jar#

场景&#xff1a;在登录的时候添添加图形验证码功能&#xff0c;使用 com.google.code.kaptcha开发图像验证码&#xff0c;。通过pom引入依赖一直红色提示&#xff0c;找打不到依赖,如图所示 原因&#xff1a;kaptcha-2.3.jar 没有放在mavan的中央仓库 解决方案&#xff0c;需…

Hive笔记

目录 第3章 Hive数据类型 第 4 章 DDL 数据定义 第5章DML数据操作 第6章 查询&#xff08;语法与MySQL一样&#xff09; 第 7 章 分区表和分桶表 第 8 章 函数 第3章 Hive数据类型 如array<map<string,int>> 集合数据类型工作中不是很常用&#xff0c;最常用…

Web 攻防之业务安全:Response状态值修改测试(修改验证码返回值 绕过限制.)

Web 攻防之业务安全&#xff1a;Response状态值修改测试 业务安全是指保护业务系统免受安全威胁的措施或手段。广义的业务安全应包括业务运行的软硬件平台&#xff08;操作系统、数据库&#xff0c;中间件等&#xff09;、业务系统自身&#xff08;软件或设备&#xff09;、业务…