假如面试官要你手写一个promise
promise
在开发中,经常需要用到promise,promise具有很多特性,这一次将对promise特性进行总结,并从零写一个promise。
步骤一
- Promise特点
- 1,创建时需要传递一个函数,否则会报错
- 2,会给传入的函数设置两个回调函数
- 3,刚创建的Promise对象状态是pending
class MyPromise {constructor(handle) {// 3,刚创建的Promise对象状态是pendingthis.status = "pending";// 1,创建时需要传递一个函数,否则会报错if (!this._isFunction(handle)) {throw new Error("请传入一个函数");}// 2,会给传入的函数设置两个回调函数handle(this._resolve.bind(this), this._reject.bind(this))}_resolve() {}_reject() {}_isFunction(fn) {return typeof fn === "function";}
}
步骤二
- Promise特点
- 4,状态一旦发生改变就不可再次改变
- 5,可以通过then来监听状态的改变
- 5.1,如果创建监听时,状态已经改变,立即执行监听回调
- 5.2,如果创建监听时,状态未改变,会等状态改变后执行
- 5.3,同一promise对象可以添加多个then监听,状态改变时按照注册顺序依次执行
// 定义常量保存对象的状态
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";class MyPromise {constructor(handle) {// 3,刚创建的Promise对象状态是pendingthis.status = PENDING;// 成功回调的值this.value = undefined;// 失败回调的值this.reason = undefined;// 注册的成功回调this.onResolvedCallbacks = [];// 注册的失败回调this.onRejectedCallbacks = [];// 1,创建时需要传递一个函数,否则会报错if (!this._isFunction(handle)) {throw new Error("请传入一个函数");}// 2,会给传入的函数设置两个回调函数handle(this._resolve.bind(this), this._reject.bind(this))}_resolve(value) {// 4,状态一旦发生改变就不可再次改变if (this.status === PENDING) {this.status = FULFILLED;this.value = value;// 5.3,同一promise对象可以添加多个then监听,状态改变时按照注册顺序依次执行this.onResolvedCallbacks.forEach(fn => fn(this.value));}}_reject(reason) {// 4,状态一旦发生改变就不可再次改变if (this.status === PENDING) {this.status = REJECTED;this.reason = reason;// 5.3,同一promise对象可以添加多个then监听,状态改变时按照注册顺序依次执行this.onRejectedCallbacks.forEach(fn => fn(this.reason));}}then(onResolved, onRejected) {// 判断有没有传入成功的回调if (this._isFunction(onResolved)) {// 5.1,如果创建监听时,状态已经改变,立即执行监听回调if (this.status === FULFILLED) {onResolved(this.value);}}// 判断有没有传入失败的回调if (this._isFunction(onRejected)) {// 5.1,如果创建监听时,状态已经改变,立即执行监听回调if (this.status === REJECTED) {onRejected(this.reason);}}// 5.2,如果创建监听时,状态未改变,会等状态改变后执行if (this.status === PENDING) {if (this._isFunction(onResolved)) {this.onResolvedCallbacks.push(onResolved);}if (this._isFunction(onRejected)) {this.onRejectedCallbacks.push(onRejected);}}}_isFunction(fn) {return typeof fn === "function";}
}
详解then方法
- 接收两个参数:成功回调,失败回调
- 如果promise失败了,但是没有注册失败监听,就会报错
- then方法每次执行完毕都会返回一个新的Promise对象
- 如果then方法只有成功回调
- 则它返回的promise的状态会继承当前promise的状态。
- 如果当前promise的状态为成功:新promise的值为当前then的成功回调的返回值。
- 如果当前promise的状态为失败:新的promise没有失败监听,则会报错
- 如果then方法同时包含成功回调、失败回调
- 则它返回的promise的状态都为成功,且值为成功或者失败回调的返回值。
- 如果then方法只有成功回调
- 回调函数的返回值
- 如果then方法的成功/失败回调返回的是promise对象
- 则then方法返回的新的promise对象的状态由新promise的内部决定。
- 且值为新promise的内resolve/reject函数传递的参数。
- 如果then方法的成功/失败回调返回的是普通数据类型
- 则then方法返回的新的promise对象的状态都为成功。
- 且值为成功/失败回调的返回值,即都会传递给新的promise对象成功的回调。
- 如果then方法的成功/失败回调没有返回值
- 同返回普通数据类型
- 如果then方法的成功/失败回调返回的是promise对象
- 失败回调函数
- 可以捕获上一个promise对象的then方法中成功回调函数执行时的异常
参考 前端进阶面试题详细解答
then(onResolved, onRejected) {return new MyPromise((nextResolve, nextReject) => {// 1.判断有没有传入成功的回调if (this._isFunction(onResolved)) {// 2.判断当前的状态是否是成功状态if (this.status === FULFILLED) {try {// 拿到上一个promise成功回调执行的结果let result = onResolved(this.value);// console.log("result", result);// 判断执行的结果是否是一个promise对象if (result instanceof MyPromise) {result.then(nextResolve, nextReject);} else {// 将上一个promise成功回调执行的结果传递给下一个promise成功的回调nextResolve(result);}} catch (e) {nextReject(e);}}}// 1.判断有没有传入失败的回调// if(this._isFunction(onRejected)){try {// 2.判断当前的状态是否是失败状态if (this.status === REJECTED) {let result = onRejected(this.reason);if (result instanceof MyPromise) {result.then(nextResolve, nextReject);} else {nextResolve(result);}}} catch (e) {nextReject(e);}// }// 2.判断当前的状态是否是默认状态if (this.status === PENDING) {if (this._isFunction(onResolved)) {// this.onResolvedCallback = onResolved;this.onResolvedCallbacks.push(() => {try {let result = onResolved(this.value);if (result instanceof MyPromise) {result.then(nextResolve, nextReject);} else {nextResolve(result);}} catch (e) {nextReject(e);}});}// if(this._isFunction(onRejected)){// this.onRejectedCallback = onRejected;this.onRejectedCallbacks.push(() => {try {let result = onRejected(this.reason);if (result instanceof MyPromise) {result.then(nextResolve, nextReject);} else {nextResolve(result);nextReject();}} catch (e) {nextReject(e);}});// }}});
}
详解catch方法
- 其实是then方法的失败回调函数的语法糖
- 如果需要同时使用then和catch方法,必须使用链式编程,不然会报错
- 可以捕获上一个promise对象的then方法中成功回调函数执行时的异常
catch(onRejected) {return this.then(undefined, onRejected);
}
为啥使用catch时最好使用链式编程
- 因为then方法只有成功回调,所以p2的状态会继承p1
- 又因为p2的状态为失败,且没有对p2进行失败监听,所以报错
let p1 = new Promise(function (resolve, reject) {// resolve();reject();
});
let p2 = p1.then(function () {console.log("成功");
});
p1.catch(function () {console.log("失败1");
});
Promise.all()
- Promise.all(params)特点
- 参数为一个数组,且数组元素为promise类型数据
- 返回值为一个promise,
- 如果所有promise都执行成功
- 返回值为所有promise都成功时返回的结果的集合
- 如果有一个promise执行失败了,则返回失败的promise
- 如果所有promise都执行成功
static all(list){return new MyPromise(function (resolve, reject) {let arr = [];let count = 0;for(let i = 0; i < list.length; i++){let p = list[i];p.then(function (value) {// arr.push(value); 注意不要这样写,会导致结果顺序不对arr[i] = valuecount++;if(list.length === count){resolve(arr);}}).catch(function (e) {reject(e);});}});
}
Promise.race()
- Promise.race(params)特点
- 参数为一个数组,且数组元素为promise类型数据
- 返回值为一个promise,且返回值为第一个成功或者失败的promise的值
static race(list){return new MyPromise(function (resolve, reject) {for(let p of list){p.then(function (value) {resolve(value);}).catch(function (e) {reject(e);});}})
}
相关文章:
假如面试官要你手写一个promise
promise 在开发中,经常需要用到promise,promise具有很多特性,这一次将对promise特性进行总结,并从零写一个promise。 步骤一 Promise特点 1,创建时需要传递一个函数,否则会报错2,会给传入的函…...

【leetcode】寻找重复数
题目链接:寻找重复数https://leetcode.cn/problems/find-the-duplicate-number/ 方法一:快慢指针 因为只有一个数字是重复的,且一个数字正好对应一个唯一的下标,所以可以将数组抽象为一个链表,假定数组为{1,2,3,4,5,…...
LeetCode 1247. Minimum Swaps to Make Strings Equal【数学,贪心,字符串】
本文属于「征服LeetCode」系列文章之一,这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁,本系列将至少持续到刷完所有无锁题之日为止;由于LeetCode还在不断地创建新题,本系列的终止日期可能是永远。在这一系列刷题文章…...
pid控制加热算法,附代码仓库
1、该项目层次化结构清晰,代码框架耦合度低,可复用性、可移植性强。 2、功能代码与底层硬件无直接关联,无需更改上层应用逻辑,只需更改接口文件,即可移植到不同的硬件平台; 3、使用lwrb开源组件、pid开源算…...
一文看懂预训练和自训练模型
说到预训练模型,不得不提迁移学习了,由于很多数据不是标签数据,人工标注非常耗时,神经网络在很多场景下受到了限制。但是迁移学习和自学习的出现,在一定程度上缓解甚至解决了这个问题。我们可以在标签丰富的场景下进行…...

(五十四)大白话索引的页存储物理结构,是如何用B+树来实现的?.md
上一次我们给大家说了主键索引的目录结构,只要在一个主键索引里包含每个数据页跟他最小主键值,就可以组成一个索引目录,然后后续你查询主键值,就可以在目录里二分查找直接定位到那条数据所属的数据页,接着到数据页里二…...
前端Vue代码风格指南
一、命名规范 市面上常用的命名规范: camelCase(小驼峰式命名法 —— 首字母小写) PascalCase(大驼峰式命名法 —— 首字母大写) kebab-case(短横线连接式) Snake(下划线连接式&…...

「TCG 规范解读」基础设施架构和协议 (2)
可信计算组织(Ttrusted Computing Group,TCG)是一个非盈利的工业标准组织,它的宗旨是加强在相异计算机平台上的计算环境的安全性。TCG于2003年春成立,并采纳了由可信计算平台联盟(the Trusted Computing Platform Alli…...

NodeJs 中的 HTML 模板
💂 个人网站:【海拥】【摸鱼游戏】【神级源码资源网】🤟 前端学习课程:👉【28个案例趣学前端】【400个JS面试题】💅 想寻找共同学习交流、摸鱼划水的小伙伴,请点击【摸鱼学习交流群】 HTML 模板是一种允许我…...

3.ffmpeg命令行环境搭建、ffmpeg命令行初步了解
在上章,我们讲过: ffmpeg.exe: 主要用于转码或者剪切的应用程序, 也可以从url/现场音频/视频源抓取输入源ffplay.exe: 主要用于播放视频的应用程序,该应用程序源码是开源的,我们后面章节会去源码分析ffprobe.exe: 主要用于分析视频码流的应用程序, 可以获取媒体文件的详细信息,…...

Kubernetes初始化容器
初始化容器 之前了解了容器的健康检查的两个探针:liveness probe(存活探针)和readiness probe(可读性探针)的使用方法,我们说在这两个探针是可以影响容器的生命周期的,包括我们之前提到的容器的…...

leetcode: Swapping Nodes in a Linked List
leetcode: Swapping Nodes in a Linked List1. 题目描述2. 题目解答3. 总结1. 题目描述 You are given the head of a linked list, and an integer k.Return the head of the linked list after swapping the values of the kth node from the beginning and the kth node f…...

Nydus 在约苗平台的容器镜像加速实践
文 | 向申 约苗平台运维工程师 关注云原生领域 本文字数 9574阅读时间24分钟 本文是来自向申同学的分享,介绍了其在 K8s 生产环境集群部署 Nydus 的相关实践。 Nydus 是蚂蚁集团,阿里云和字节等共建的开源容器镜像加速项目,是 CNCF Dragon…...

企业对不同形态CRM系统价格需求不同
很多企业在选型时关心CRM客户管理系统的价格,有人对CRM的价格完全没有概念,也有的人先问价格再看其他。CRM价格在系统选型中到底有多重要?不同类型CRM系统的价格是否有所不同? CRM的不同产品形态也会影响价格 通常情况下&#x…...
「JVM 高效并发」线程安全
面向过程编程,把数据和过程分别作为独立的部分考虑,数据代表问题空间中的客体,程序代码则用于处理这些数据;面向对象编程,把数据和行为都看做对象的一部分,以符合现实世界的思维方式来编写和组织程序&#…...

微信扫码登录
一、准备工作 微信开发者平台:https://open.weixin.qq.com 1、注册 2、邮箱激活 3、完善开发者资料 4、开发者资质认证:仅能企业注册(后面提供学习的使用渠道)准备营业执照,1-2个工作日审批、300元 5、创建网站应用&…...
Unity协程的简单应用
Unity协程是一种特殊的函数,可以让你在Unity中创建一种类似于多线程的异步操作。它可以在需要等待某个操作完成时,暂停执行当前代码,等待某个条件满足后再继续执行。 在一般情况下 unity中调用函数时,函数将运行到完成状态&#x…...
LeetCode 1250. Check If It Is a Good Array【数论】
本文属于「征服LeetCode」系列文章之一,这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁,本系列将至少持续到刷完所有无锁题之日为止;由于LeetCode还在不断地创建新题,本系列的终止日期可能是永远。在这一系列刷题文章…...

ETHDenver 2023
ETHDenver是全球最大、持续时间最长的以太坊活动之一,今年的活动定于2月24日至3月5日在美国科罗拉多州丹佛市盛大举行。这次活动将面向以太坊和其他区块链协议爱好者、设计者和开发人员。Moonbeam作为ETHDenver 2023的Meta赞助商,将在本次活动中展示令人…...

React架构演变
老版React架构 React 16之前的架构 其实就分为两个部分: Reconciler协调器Render渲染器 Reconciler协调器负责本次更新有什么组件需要被渲染,diff算法就发生在这个步骤中,在diff算法中会将上次更新的组件和本次更新的组件做一个对比&…...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...

简易版抽奖活动的设计技术方案
1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...

K8S认证|CKS题库+答案| 11. AppArmor
目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...

【网络安全产品大调研系列】2. 体验漏洞扫描
前言 2023 年漏洞扫描服务市场规模预计为 3.06(十亿美元)。漏洞扫描服务市场行业预计将从 2024 年的 3.48(十亿美元)增长到 2032 年的 9.54(十亿美元)。预测期内漏洞扫描服务市场 CAGR(增长率&…...
JVM垃圾回收机制全解析
Java虚拟机(JVM)中的垃圾收集器(Garbage Collector,简称GC)是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象,从而释放内存空间,避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...
多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验
一、多模态商品数据接口的技术架构 (一)多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如,当用户上传一张“蓝色连衣裙”的图片时,接口可自动提取图像中的颜色(RGB值&…...

微服务商城-商品微服务
数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...
鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/
使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题:docker pull 失败 网络不同,需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...
安卓基础(aar)
重新设置java21的环境,临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的: MyApp/ ├── app/ …...

使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...