【手写promise——基本功能、链式调用、promise.all、promise.race】

news2024/11/17 3:31:26

文章目录

  • 前言
  • 一、前置知识
  • 二、实现基本功能
  • 二、实现链式调用
  • 三、实现Promise.all
  • 四、实现Promise.race
  • 总结


前言

关于动机,无论是在工作还是面试中,都会遇到Promise的相关使用和原理,手写Promise也有助于学习设计模式以及代码设计。
本文主要介绍了如何使用自己的代码去实现Promise的一些功能。


以下是本篇文章正文内容

一、前置知识

手写之前,需要知道promise有哪些基本特性:

1、promise有三种状态:pending、fulfilled、rejected。

2、promise的默认状态是pending,状态流转有两种:pending—>fulfilled、pending—>rejected。
在这里插入图片描述

3、实例化Promise时,可以接收一个函数作为参数,这个函数有两个参数,分别是resolve、reject方法用于改变实例状态。并能够执行下一个then中的回调函数。
4、then方法中接收两个函数作为参数,第一个函数相当于执行上一个 promise 中执行 resolve 后对应的回调,第二个函数相当于执行上一个 promise 中执行 reject 后对应的回调。

// 1. 实例化 Promise 时
// 可以接收一个函数作为参数,这个函数可以接收到 resolve 和 reject 两个实例方法
// 用于更改当前实例的状态,并把它们接收到的参数传递给下一个 then 对应的参数中
  new Promise((resolve, reject) => {
    // resolve 和 reject 可以都执行,但都执行的意义不大,因为 promise 状态发生更改后,就不能在被更改
    resolve('ok');
    // reject('err');
  }).then((value) => {
    console.log("resolve callback = ", value); // 若执行 resolve,则 value = ok
  }, (reason) => {
    console.log("reject callback = ", reason); // 若执行 reject,则 value = err
  });

5、then操作后会返回一个新的Promise,且支持链式调用。

let promise = new Promise((resolve,reject) => {
    resolve(11)
})
const a = new Promise((resolve,reject) => {
    reject('ok')
})
promise.then(res => {
    console.log(res)
    return a
}).then(res => console.log(res))

6、Promise.all以数组的形式接收多个Promise,当所有Promise执行完成,且状态都为fulfilled时,返回执行成功的结果;有一个失败,则返回失败的结果。

7、Promise.race以数组的形式接收多个Promise,只要有一个Promise先执行成功,无论什么状态,都返回这个结果,给到下一个then中对应的回调。

二、实现基本功能

1、

  • new 操作
  • resolve、reject方法
  • then方法
// 定义三种状态
const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';

class Promise {
    constructor(executor) {

         // 1、默认状态 - PENDING
         this.status = PENDING;
         // 2、内部维护的变量值
         this.value = undefined;
         this.reason = undefined;

        try {
            executor(this.resolve.bind(this), this.reject.bind(this)) // 实例化传进来的函数会立即执行
        } catch(err) {
            this.reject(err)
        }
    }
    resolve(value) {
        if (this.status === PENDING) {
            this.value = value;
            this.status = FULFILLED;
        }
    }
    reject(reason) {
        if (this.status === PENDING) {
            this.reason = reason;
            this.status = REJECTED;
        }
    }
    then(onFulfilled, onRejected) {
        if (onFulfilled && this.status === FULFILLED) {
            let res = onFulfilled(this.value)
            this.resolvePromise(res, resolve, reject)
        }
        if (onRejected && this.status === REJECTED) {
            let res = onRejected(this.reason)
            this.resolvePromise(res, resolve, reject)
        }        
    }
}

通过以下代码自测:

new Promise((resolve,reject) => {
    resolve(1)
}).then(res=> {
	console.log(res) // 1
})

2、考虑异步的情况。
以上代码都是同步的,Promise实例化时传入了异步函数如setTimeout,在setTimeout中resolve,在下一个then的回调函数获取异步函数的状态呢?

我们可以使用回调队列缓存起来,等待执行:

// 定义三种状态
const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';

class Promise {
    constructor(executor) {

         // 1、默认状态 - PENDING
         this.status = PENDING;
         // 2、内部维护的变量值
         this.value = undefined;
         this.reason = undefined;
         this.onResolveCallBack = [];// 缓存 onResolve 
    	 this.onRejectCallBack = [];// 缓存 onReject 

        try {
            executor(this.resolve.bind(this), this.reject.bind(this)) // 实例化传进来的函数会立即执行
        } catch(err) {
            this.reject(err)
        }
    }
    resolve(value) {
        if (this.status === PENDING) {
            this.value = value;
            this.status = FULFILLED;
            // 遍历调用 onResolveCallBack
      		this.onResolveCallBack.forEach(fn => fn());
        }
    }
    reject(reason) {
        if (this.status === PENDING) {
            this.reason = reason;
            this.status = REJECTED;
            this.onResolveCallBack.forEach(fn => fn());
        }
    }
    then(onFulfilled, onRejected) {
        if (onFulfilled && this.status === FULFILLED) {
            let res = onFulfilled(this.value)
            this.resolvePromise(res, resolve, reject)
        }
        if (onRejected && this.status === REJECTED) {
            let res = onRejected(this.reason)
            this.resolvePromise(res, resolve, reject)
        }
        // 当前 promise 状态为 pending,把当前的 onResolve & onReject 缓存起来
        if (this.status === PENDING) {
        	this.onResolveCallBack.push(() => {
        		onResolve(this.value);
        	});	
		    this.onRejectCallBack.push(() => {
		      onReject(this.value);
		    });
  		}       
    }
}

用以下代码测试通过:

 let p = new MyPromise((resolve, reject) => {
      setTimeout(()=>{
        resolve(1);
      },1000);
    }).then(value => {
      console.log('then resolve = ', value);
    }, reason => {
      console.log('then reject = ', reason);
    });
// then resolve = 1

二、实现链式调用

then(onFulfilled, onRejected) {
   	return new Promise((resolve, reject) => { // 支持链式调用
         if (onFulfilled && this.status === FULFILLED) {
             let res = onFulfilled(this.value)
             resolve(res);
         }
         if (onRejected && this.status === REJECTED) {
             let res = onRejected(this.reason)
             resolve(res);
         }
         if (this.status === PENDING) {
             this.onResolvedCallbacks.push(() =>{
                 let res = onFulfilled(this.value)
                 resolve(res);
             })
             this.onRejectedCallbacks.push(() => {
                 let res = onRejected(this.reason)
                 resolve(res);
             })
         }
     })   
 }

测试用例:

let promise = new Promise((resolve,reject) => {
    resolve(1)
})
promise.then(res => {
    return 2 // 返回普通对象
}).then(res => console.log(res)) // 2

如果在then中返回的是普通对象,上述代码能满足,如果返回的是一个Promise,则还需要补充:

then(onFulfilled, onRejected) {
    return new Promise((resolve, reject) => { // 支持链式调用
         if (onFulfilled && this.status === FULFILLED) {
             let res = onFulfilled(this.value)
             this.resolvePromise(res, resolve, reject)
         }
         if (onRejected && this.status === REJECTED) {
             let res = onRejected(this.reason)
             this.resolvePromise(res, resolve, reject)
         }
         if (this.status === PENDING) {
             this.onResolvedCallbacks.push(() =>{
                 let res = onFulfilled(this.value)
                 this.resolvePromise(res, resolve, reject)
             })
             this.onRejectedCallbacks.push(() => {
                 let res = onRejected(this.reason)
                 this.resolvePromise(res, resolve, reject)
             })
         }
     })
     
 }
 resolvePromise(res, resolve, reject) {
     if(res instanceof Promise) { // 对then中返回Promise做处理
         res.then(resolve, reject)
       } else{
         // 普通值
         resolve(res)
     }
 }

测试用例:

let promise = new Promise((resolve,reject) => {
    resolve(11)
})
const a = new Promise((resolve,reject) => {
    reject('ok')
})
promise.then(res => {
   return a;
}).then(res => console.log(res)) // ok

三、实现Promise.all

由上述前置知识可知,Promise.all是全部成功才返回成功的结果,有一个失败就返回失败的结果:

Promise.all = function(arr) {
    return new Promise((resolve, reject) => {
        if (!Array.isArray(arr)) {
            return reject("参数必须为数组");
        }
        let successNum = 0; // 成功的Promise条数
        let resultArray = [];
        let totalNum = arr.length; // 总Promise数量
        let isFail = false;
        arr.forEach(item => {
            item.then(res => {
                successNum++;
                resultArray.push(res);
                if (successNum === totalNum) { // 说明是全部成功
                    return resolve(resultArray)
                }
            }, err => {
               if (!isFail) reject(err)
               isFail = true;
            })
        });       
    })
}

测试用例:

function wait500(input) {
	return new Promise((resolve, reject) => {
		setTimeout(()=> {
			resolve(500)
		}, 500)
	})
}
function wait1000(input) {
	return new Promise((resolve, reject) => {
		setTimeout(()=> {
			resolve(1000)
		}, 1000)
	})
}
// 全部执行完成之后回调
Promise.all([wait1000(), wait500()]).then(result => {
	console.log('all end', result)
}, err => {
    console.log('fafaf', err)
}) // 'all end [500, 1000]'

四、实现Promise.race

由上述前置知识可知,Promise.race是有一个promise先执行成功,无论什么状态,都返回这个结果:

Promise.race = function(arr) {
    return new Promise((resolve, reject) => {
        if (!Array.isArray(arr)) {
            return reject("参数必须为数组");
        }
        arr.forEach(item => {
            item.then(res => {
                return resolve(res); // 状态不可逆,只要其中一个Promise转变了状态,后续状态就不会发生改变
            }, err => {
                reject(err);
            })
        });       
    })

}

测试用例:

function wait500(input) {
	return new Promise((resolve, reject) => {
		setTimeout(()=> {
			resolve(500)
		}, 500)
	})
}
function wait1000(input) {
	return new Promise((resolve, reject) => {
		setTimeout(()=> {
			resolve(1000)
		}, 1000)
	})
}
Promise.race([wait1000(), wait500()]).then(result => {
	console.log('all end', result)
}, err => {
    console.log('fafaf', err)
}) // 打印出'all end 500'

总结

总的代码:

const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';

class Promise {
    constructor(executor) {

         // 1、默认状态 - PENDING
         this.status = PENDING;
         // 2、内部维护的变量值
         this.value = undefined;
         this.reason = undefined;

         // 存放回调
        this.onResolvedCallbacks = [];
        this.onRejectedCallbacks = [];
        try {
            executor(this.resolve.bind(this), this.reject.bind(this))
        } catch(err) {
            this.reject(err)
        }
    }
    resolve(value) {
        if (this.status === PENDING) {
            this.value = value;
            this.status = FULFILLED;
            this.onResolvedCallbacks.forEach(fn => fn())
        }

    }
    reject(reason) {
        if (this.status === PENDING) {
            this.reason = reason;
            this.status = REJECTED;
            this.onRejectedCallbacks.forEach(fn => fn())
        }
    }
    then(onFulfilled, onRejected) {
        return new Promise((resolve, reject) => { // 支持链式调用
            if (onFulfilled && this.status === FULFILLED) {
                let res = onFulfilled(this.value)
                this.resolvePromise(res, resolve, reject)
            }
            if (onRejected && this.status === REJECTED) {
                let res = onRejected(this.reason)
                this.resolvePromise(res, resolve, reject)
            }
            if (this.status === PENDING) {
                this.onResolvedCallbacks.push(() =>{
                    let res = onFulfilled(this.value)
                    this.resolvePromise(res, resolve, reject)
                })
                this.onRejectedCallbacks.push(() => {
                    let res = onRejected(this.reason)
                    this.resolvePromise(res, resolve, reject)
                })
            }
        })
        
    }
    resolvePromise(res, resolve, reject) {
        if(res instanceof Promise) {
            res.then(resolve, reject)
          } else{
            // 普通值
            resolve(res)
        }
    }
}
// 全部成功才返回成功的结果,有一个失败就返回失败的结果
Promise.all = function(arr) {
    return new Promise((resolve, reject) => {
        if (!Array.isArray(arr)) {
            return reject("参数必须为数组");
        }
        let successNum = 0;
        let resultArray = [];
        let totalNum = arr.length;
        let isFail = false;
        arr.forEach(item => {
            item.then(res => {
                successNum++;
                resultArray.push(res);
                if (successNum === totalNum) {
                    return resolve(resultArray)
                }
            }, err => {
                if (!isFail) reject(err)
                isFail = true;
            })
        });       
    })
}

// 某个promise先完成,
Promise.race = function(arr) {
    return new Promise((resolve, reject) => {
        if (!Array.isArray(arr)) {
            return reject("参数必须为数组");
        }
        arr.forEach(item => {
            item.then(res => {
                return resolve(res);
            }, err => {
                reject(err);
            })
        });       
    })

}

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

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

相关文章

9个python自动化脚本,PPT批量生成缩略图、添加图片、重命名

引言 最近一番在整理资料,之前买的PPT资源很大很多,但归类并不好,于是一番准备把这些PPT资源重新整理一下。统计了下,这些PPT资源大概有2000多个,一共30多G,一个一个手动整理这个投入产出比也太低了。 作为…

CotEditor for mac 4.0.1 中文版(开源文本编辑器)

coteditorformac是一款简单实用的基于Cocoa的macOS纯文本编辑器,coteditormac版本可以用来编辑网页、结构化文本、程序源代码等文本文件,使用起来非常方便。 CotEditor for Mac具有正则表达式搜索和替换、语法高亮、编码等实用功能,而CotEdi…

QtWidgets和QtQuick融合(QML与C++融合)

先放一个界面效果吧! 说明:该演示程序为一个App管理程序,可以将多个App进行吸入管理。 (动画中的RedRect为一个带有QSplashScreen的独立应用程序) 左侧边栏用的是QQuickView进行.qml文件的加载(即QtQuick…

JS算法之树(一)

前言 之前我们已经介绍过一种非顺序数据结构,是散列表。 JavaScript散列表及其扩展http://t.csdn.cn/RliQf 还有另外一种非顺序数据结构---树。 树数据结构 树是一种分层数据的抽象模型。公司组织架构图就是常见的树的例子。 相关术语 一个树结构&#xff0…

x86的内存寻址方式

文章目录 一、实模式寻址二、保护模式寻址三、段页式内存管理四、Linux的内存寻址五、进程与内存1、内核空间和用户空间2、内存映射3、进程内存分配与回收 一、实模式寻址 在16位的8086时代,CPU为了能寻址超过16位地址能表示的最大空间(因为 8086 的地址…

小研究 - J2EE 应用服务器的软件老化测试研究

软件老化现象是影响软件可靠性的重要因素,长期运行的软件系统存在软件老化现象,这将影响整个业务系统的正常运行,给企事业单位带来无可估量的经济损失。软件老化出现的主要原因是操作系统资源消耗殆尽,导致应用系统的性能下降甚至…

virtuoso61x中集成calibre

以virtuoso618为例,在搭建完电路、完成前仿工作之后绘制版图,版图绘制完成之后需要进行drc和lvs【仅对于学校内部通常的模拟后端流程而言】,一般采用mentor的calibre来完成drc和lvs。 服务器上安装有virtuoso和calibre,但是打开la…

servlet,Filter,责任的设计模式,静态代理

servlet servlet是前端和数据库交互的一个桥梁 静态网页资源的技术:在前端整个运行的过程中 我们的网页代码不发生改变的这种情况就称为静态的网页资源技术动态网页资源的技术:在前端运行的过程中 我们的前端页面代码会发生改变的这种情况就称为 动态的网…

电商版面设计之优惠券设计

1、画一个矩形---最快的方法,提前写好 2、ALT复制矩形图层 3、提前把优惠券的文案准备好 4、改一下字体---72 5、字体改成12号字体 6、上面对齐选择第二个去做,最上方 7、后面那个就是门槛 8、用Alt复制4个 9、改字就行 10、看见不错的优惠劵设计可以参…

word如何调整页码

文章目录 如何调整页码 如何调整页码 用 word 写报告的时候,经常遇到要求说是要从正文开始才显示页码,那如何实现呢 把鼠标放在我们正文的那一页的顶部,点击 布局 ,再点击分隔符,再点击连续 再点击编译页脚 选择你想要的页脚格式…

十四、pikachu之XSS

文章目录 1、XSS概述2、实战2.1 反射型XSS(get)2.2 反射型XSS(POST型)2.3 存储型XSS2.4 DOM型XSS2.5 DOM型XSS-X2.6 XSS之盲打2.7 XSS之过滤2.8 XSS之htmlspecialchars2.9 XSS之href输出2.10 XSS之JS输出 1、XSS概述 Cross-Site S…

探讨uniapp的组件使用的问题

1 view Flex是Flexible Box的缩写,意为“弹性布局”,用来为盒状模型提供最大的灵活性。 当设置display: flex后,继续给view等容器组件设置flex-direction:row或column,就可以在该容器内按行或列排布子组件。uni-app推荐使用flex布…

[Linux]进程

文章目录 1. 进程控制1.1 进程概述1.1.1 并行和并发1.1.2 PCB1.1.4 进程状态1.1.5 进程命令 1.2 进程创建1.2.1 函数1.2.2 fork() 剖析 1.3 父子进程1.3.1 进程执行位置1.3.2 循环创建子进程1.3.3 终端显示问题1.3.4 进程数数 1.4 execl和execlp函数1.4.1 execl()1.4.2 execlp(…

Android 13.0 首次开机默认授予app运行时权限(去掉运行时授权弹窗)

1.概述 在13.0的系统产品开发中,在android6.0以后对于权限的申请,都需要动态申请,所以会在系统首次启动后,在app的首次运行时,会弹出授权窗口,会让用户手动授予app运行时权限,在由于系统产品开发需要要求默认授予app运行时权限,不需要用户默认授予运行时弹窗,所以需要…

基于OpenCV的迷宫路径查找

附上代码: import cv2 import numpy as np# 读取图像 img cv2.imread("img_3.png") thres_min 150 # 二值化最小阈值if not img is None:# 二值化处理ret, img cv2.threshold(img, thres_min, 255, cv2.THRESH_BINARY)cv2.imshow("img_thres&qu…

【C++】list类的模拟实现

🏖️作者:malloc不出对象 ⛺专栏:C的学习之路 👦个人简介:一名双非本科院校大二在读的科班编程菜鸟,努力编程只为赶上各位大佬的步伐🙈🙈 目录 前言一、list类的模拟实现1.1 list的…

使用Python写入数据到Excel:实战指南

在数据科学领域,Excel是一种广泛使用的电子表格工具,可以方便地进行数据管理和分析。然而,当数据规模较大或需要自动化处理时,手动操作Excel可能会变得繁琐。此时,使用Python编写程序将数据写入Excel文件是一个高效且便…

如何推广你的开源项目?

🌷🍁 博主猫头虎 带您 Go to New World.✨🍁 🦄 博客首页——猫头虎的博客🎐 🐳《面试题大全专栏》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺 &a…

mongodb聚合排序的一个巨坑

现象: mongodb cpu动不动要100%,如下图 分析原因: 查看慢日志发现,很多条这样的查询,一直未执行行完成,占用大量的CPU [{$match: {"tags.taskId": "64dae0a9deb52d2f9a1bd71e",grnty: …

电商版面设计之首页设计

首页设计资料 1、首页----多看大美工 2、手表首页 3、水密码官方旗舰店 4、AK男装 5、百雀羚首页设计 6、活动专区 7、店铺有一些活动,会在里面进行体现 8、提前构思,多看别人的店铺设计,是提升自己店铺设计最好的方法 9、产品专区 10、买一送…