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

文章目录

  • 前言
  • 一、前置知识
  • 二、实现基本功能
  • 二、实现链式调用
  • 三、实现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、默认状态 - PENDINGthis.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、默认状态 - PENDINGthis.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;// 遍历调用 onResolveCallBackthis.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、默认状态 - PENDINGthis.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.mzph.cn/news/54313.shtml

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

相关文章

nethogs交叉编译

前文: 网上实在找不到交叉编译的案例,只能自己手动搞了 参考:iftop交叉编译(ncurses-5.9、pcap-1.8.1、iftop-1.0)_iftop 编译_sinat_25505501的博客-CSDN博客 参照上述链接,自己编译好ncurses和libpcap 我这边用的是 ncurses6…

leetcode分类刷题:矩阵顺时针模拟

1、这种题目是对代码熟练度考察,模拟顺时针建立或访问矩阵,需要注意矩阵是否为方阵 2、具体思路:以圈数为循环条件,每一圈都坚持左闭右开的区间规则;当小的行列值为奇数,最后一圈为一行或一列或一个数字的不…

WPF基础入门-Class5-WPF命令

WPF基础入门 Class5-WPF命令 1、xaml编写一个button&#xff0c;Command绑定一个命令 <Grid><ButtonWidth"100"Height"40" Command"{Binding ShowCommand}"></Button> </Grid>2、编写一个model.cs namespace WPF_Le…

我的编程学习过程

自信与经验 在毕业的时候就觉得繁体字很难。大陆都在使用简体字&#xff0c;戴季陶说这是在亡国&#xff0c;没有这么严 重。繁体字会意&#xff0c;简体字简单&#xff0c;中国文盲很多&#xff0c;为了加快经济建设的步伐&#xff0c;不得不牺牲很多 东西。为了解决温饱&…

【LeetCode-面试经典150题-day15】

目录 104.二叉树的最大深度 100.相同的树 226.翻转二叉树 101.对称二叉树 105.从前序与中序遍历序列构造二叉树 106.从中序与后序遍历序列构造二叉树 117.填充每个节点的下一个右侧节点指针Ⅱ 104.二叉树的最大深度 题意&#xff1a; 给定一个二叉树 root &#xff0c;返回其…

STM32F103 4G Cat.1模块EC200S使用

一、简介 EC200S-CN 是移远通信最近推出的 LTE Cat 1 无线通信模块&#xff0c;支持最大下行速率 10Mbps 和最大上行速率 5Mbps&#xff0c;具有超高的性价比&#xff1b;同时在封装上兼容移远通信多网络制式 LTE Standard EC2x&#xff08;EC25、EC21、EC20 R2.0、EC20 R2.1&a…

用大白话来讲讲多线程的知识架构

感觉多线程的知识又多又杂&#xff0c;自从接触java&#xff0c;就在一遍一遍捋脉络和深入学习。现在将这次的学习成果展示如下。 什么是多线程&#xff1f; 操作系统运行一个程序&#xff0c;就是一个线程。同时运行多个程序&#xff0c;就是多线程。即在同一时间&#xff0…

基于FPGA的Lorenz混沌系统verilog开发,含testbench和matlab辅助测试程序

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 将vivado的仿真结果导入到matlab显示三维混沌效果&#xff1a; 2.算法运行软件版本 vivado2019.2 matlab2022a 3.部分核心程序 testbench如下所…

npm常用命令 + 前端常用的包管理工具 以及 npm淘宝镜像配置等

npm常用命令 前端常用的包管理工具 以及 npm淘宝镜像配置等 1. 前言1.1 NodeJs的下载安装1.2 windows上1.3 常用包管理工具 2. npm2.1 npm 的安装2.2 npm初始化包2.3 npm 安装、卸载包2.3.1 非全局安装2.3.1.1 单个包的安装2.3.1.1.1 默认版本安装2.3.1.1.2 指定版本安装 2.3.…

【flutter直接上传图片到阿里云OSS】

flutter直接上传文件到阿里云需要获取凭证&#xff0c;通过调用阿里云获取凭证的接口能拿到下面这些参数 {"StatusCode": 200,"AccessKeyId": "STS.NSsrKZes4cqm.....","AccessKeySecret": "7eGnLZaEFsRCGYJAnrtdE9n....."…

解除用户账户控制提醒

解决用户账户控制提醒 1. 前言2. 解决用户账户控制提醒2.1 控制面板2.2 注册表2.3 UAC服务 结束语 1. 前言 当我们使用电脑时&#xff0c;有时进行安装应用或者打开应用时&#xff0c;总会弹出一个提示框&#xff0c;要选择点击是否允许程序运行&#xff1b; 系统经常弹出用户…

el-form表单el-form-item prop一次验证两个值

1.表单添加两个框&#xff0c;prop写上 <el-form :model"ruleForm" :rules"rules" ref"ruleForm" label-width"100px" > <el-form-item type"type" label"时间" prop"dateSectOne"><di…

【Git】测试持续集成——Git+Gitee+PyCharm

文章目录 概述一、使用Gitee1. 注册账号2. 绑定邮箱3. 新建仓库4. 查看项目地址 二、安装配置Git1. 下载安装包2. 校验是否安装成功。3. 配置Git4. Git命令5. Git实操 三、PyCharmGit1. 配置Git2. Clone项目3. 提交文件到服务器4. 从服务器拉取文件 概述 持续集成&#xff08;…

docker 启动命令

cd /ycw/docker docker build -f DockerFile -t jshepr:1.0 . #前面测试docker已经介绍过该命令下面就不再介绍了 docker images docker run -it -p 7003:9999 --name yyy -d jshepr:1.0 #上面运行报错 用这个 不报错就不用 docker rm yyy docker ps #查看项目日志 docker …

【javaweb】学习日记Day4 - Maven 依赖管理 Web入门

目录 一、Maven入门 - 管理和构建java项目的工具 1、IDEA如何构建Maven项目 2、Maven 坐标 &#xff08;1&#xff09;定义 &#xff08;2&#xff09;主要组成 3、IDEA如何导入和删除项目 二、Maven - 依赖管理 1、依赖配置 2、依赖传递 &#xff08;1&#xff09;查…

Docker容器学习:Dockerfile制作Web应用系统nginx镜像

目录 编写Dockerfile 1.文件内容需求&#xff1a; 2.编写Dockerfile&#xff1a; 3.开始构建镜像 4.现在我们运行一个容器&#xff0c;查看我们的网页是否可访问 推送镜像到私有仓库 1.把要上传的镜像打上合适的标签 2.登录harbor仓库 3.上传镜像 编写Dockerfile 1.文…

2000-2021年地级市产业升级、产业结构高级化面板数据

2000-2021年地级市产业升级、产业结构高级化面板数据 1、时间&#xff1a;2000-2021年 2、范围&#xff1a;地级市 3、指标&#xff1a;年份、地区、行政区划代码、地区、所属省份、地区生产总值、第一产业增加值、第二产业增加值、第三产业增加值、第一产业占GDP比重、第二…

常见的时序数据库

1.概念 时序数据库全称为时间序列数据库。时间序列数据库指主要用于处理带时间标签&#xff08;按照时间的顺序变化&#xff0c;即时间序列化&#xff09;的数据&#xff0c;带时间标签的数据也称为时间序列数据。 时间序列数据主要由电力行业、化工行业、气象行业、地理信息…

十四、pikachu之XSS

文章目录 1、XSS概述2、实战2.1 反射型XSS&#xff08;get&#xff09;2.2 反射型XSS&#xff08;POST型&#xff09;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…

分享一种针对uni-app相对通用的抓包方案

PART1&#xff0c;前言 近年来混合开发APP逐渐成为主流的开发模式&#xff0c;与传统的开发模式相比混合开发极大的提升了开发效率&#xff0c;同时跨平台的特性也降低了开发成本&#xff0c;一直以来混合开发被诟病的性能问题随着技术的发展也得到改善。技术的发展往往是一把…