手写一个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.kler.cn/a/2842.html

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

相关文章

maya python 中的maya.cmds 与maya.mel模块的区别笔记

作为一个python的小白,之前多从事mel程序开发,因为python变化太快,不如maya内置mel语言稳定,所以一直懒于python学习,后来偶尔使用中发现,其实python作为maya mel的封装,与内置的mel还是有很大差…

新闻文本分类任务:使用Transformer实现

❤️觉得内容不错的话,欢迎点赞收藏加关注😊😊😊,后续会继续输入更多优质内容❤️👉有问题欢迎大家加关注私戳或者评论(包括但不限于NLP算法相关,linux学习相关,读研读博…

A.机器学习入门算法(六)基于天气数据集的XGBoost分类预测

1.机器学习算法(六)基于天气数据集的XGBoost分类预测 本项目链接:https://www.heywhale.com/home/column/64141d6b1c8c8b518ba97dcc 1.1 XGBoost的介绍与应用 XGBoost是2016年由华盛顿大学陈天奇老师带领开发的一个可扩展机器学习系统。严…

用嘴写代码?继ChatGPT和NewBing之后,微软又开始整活了,Github Copilot X!

用嘴写代码?继ChatGPT和NewBing之后,微软又开始整活了,Github Copilot X! AI盛行的时代来临了,在这段时间,除了爆火的GPT3.5后,OpenAI发布了GPT4版本,同时微软也在Bing上开始加入了A…

第十四届蓝桥杯三月真题刷题训练——第 21 天

目录 第 1 题:灭鼠先锋 问题描述 运行限制 代码: 思路: 第 2 题:小蓝与钥匙 问题描述 答案提交 运行限制 代码: 思路 : 第 3 题:李白打酒加强版 第 4 题:机房 第 1 题&#xff1…

【尝鲜版】ChatGPT插件开发指南

3月23日,OpenAI官方发布了一则公告,宣告ChatGPT已经支持了插件功能,现在处于内测阶段。插件的意义不仅仅在于功能的扩展,它直接让ChatGTP拥有了联网的能力!简直是猛兽出笼、蛟龙出海,要让ChatGPT大杀特杀啊…

无需公网IP,远程连接SQL Server数据库【内网穿透】

文章目录1.前言2.本地安装和设置SQL Server2.1 SQL Server下载2.2 SQL Server本地连接测试2.3 Cpolar内网穿透的下载和安装2.3 Cpolar内网穿透的注册3.本地网页发布3.1 Cpolar云端设置3.2 Cpolar本地设置4.公网访问测试5.结语1.前言 数据库的重要性相信大家都有所了解&#xf…

【Unityc#专题篇】之c#基础篇

👨‍💻个人主页:元宇宙-秩沅 👨‍💻 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍💻 本文由 秩沅 原创 👨‍💻 收录于专栏:uni…

ASO优化之应用商店中的A/B测试——改良版

A/B 测试在应用营销中发挥着重要作用,是根据实际用户行为不断改进应用商店列表的最佳方式。 A/B 测试包括将相同的受众分成相同大小的两组实验。第一组用户能够看到测试的A变体和原始资产,第二组用户能够看到带有要测试资产的B变体。运行一段时间后&…

菜鸟刷题Day5

⭐作者:别动我的饭 ⭐专栏:菜鸟刷题 ⭐标语:悟已往之不谏,知来者之可追 一.一维数组的动态和:1480. 一维数组的动态和 - 力扣(LeetCode) 描述 给你一个数组 nums 。数组「动态和」的计算公式…

FPGA打砖块游戏设计(有上板照片)VHDL

这是一款经典打砖块游戏,我们的努力让它更精致更好玩,我们将它取名为打砖块游戏(Flyball),以下是该系统的一些基本功能:  画面简约而经典,色彩绚丽而活泼,动画流畅  玩家顺序挑战3个不同难度的级别,趣味十足  计分功能,卡通字母数字  4条生命值,由生命条显示…

React 入门(超详细)

目录前言:一、React 简介1. 什么是 React2. React 的特点3. React 高效的原因4. React 官网5. React的主要原理6. Facebook为什么要建造React?二、React 的基本使用1. 基础代码2. 效果3. 相关 js 库4. 创建虚拟DOM的两种方式5. 虚拟DOM与真实DOM6. 虚拟DOM与真实DO…

从零开始搭建游戏服务器 第一节 创建一个简单的服务器架构

目录引言技术选型正文创建基础架构IDEA创建项目添加Netty监听端口编写客户端进行测试总结引言 由于现在java web太卷了,所以各位同行可以考虑换一个赛道,做游戏还是很开心的。 本篇教程给新人用于学习游戏服务器的基本知识,给新人们一些学习…

基于springboot框架实现校园博客系统【源码+论文】展示

基于springboot框架实现校园博客系统【源码论文】开发语言:Java 框架:springboot JDK版本:JDK1.8 服务器:tomcat7 数据库:mysql 5.7 数据库工具:Navicat11 开发软件:eclipse/myeclipse/idea Mav…

HTML5 Canvas

HTML5 Canvas <canvas>元素是HTML5中的新元素&#xff0c;通过使用该元素&#xff0c;你可以在网页中绘制所需的图形。 标签定义图形&#xff0c;比如图表和其他图像&#xff0c;您必须使用脚本来绘制图形。在画布上&#xff08;Canvas&#xff09;画一个红色矩形&#…

Afterlogic Aurora Corporate Crack

Afterlogic Aurora Corporate Crack 密码重置功能已添加到“cPanel”。 已经为Plesk和DirectAdmin添加了用户注册。 Sieve过滤器中添加了表情符号支持。 添加了日历事件的默认提醒。 “生存期”选项已添加到文件存储中的公共链接中。 添加了完整的RocketChat 5.*支持。 添加了灵…

python例程:五子棋(控制台版)程序

目录《五子棋&#xff08;控制台版&#xff09;》程序使用说明程序示例代码可执行程序及源码下载路径《五子棋&#xff08;控制台版&#xff09;》程序使用说明 在PyCharm中运行《五子棋&#xff08;控制台版&#xff09;》即可进入如图1所示的系统主界面。 图1 游戏主界面 具…

Linux命令运行原理shell和bash

目录前言什么是shell,什么是bash?ls -l 执行过程前言 学习操作系统的过程中我们经常在自己的shell中执行一些Linux命令&#xff0c;那么当我们输入一个类似于 ls -a 这样的命令式&#xff0c;发生了什么&#xff1f; 换句话说&#xff0c;从我们在shell中输入ls -a 按下回车…

AJAX,Axios,JSON简单了解

一. AJAX简介概念: AJAX(Asynchronous JavaScript And XML): 异步的JavaScript 和XMLAJAX作用:1.与服务器进行数据交换: 通过AJAX可以给服务器发送请求&#xff0c;并获取服务器响应的数据使用了AJAX和服务器进行通信&#xff0c;就可以使用 HTMLAJAX来替换JSP页面了2.异步交互…

看齐iOS砍掉祖传功能,Android 16G内存也危险了

手机内存发展是真的迅速&#xff0c;12GB 没保持几年现在又朝着 16GB 普及。 相比 iOS 的墓碑机制&#xff0c;Android 后台就主打一个真实&#xff0c;只是可惜 APP 不那么老实。 如果你较早接触 Android 机&#xff0c;各种系统管理、优化 APP 的一键加速、清理应该还历历在…
最新文章