假如面试官要你手写一个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算法中会将上次更新的组件和本次更新的组件做一个对比&…...
浅谈 React Hooks
React Hooks 是 React 16.8 引入的一组 API,用于在函数组件中使用 state 和其他 React 特性(例如生命周期方法、context 等)。Hooks 通过简洁的函数接口,解决了状态与 UI 的高度解耦,通过函数式编程范式实现更灵活 Rea…...
利用ngx_stream_return_module构建简易 TCP/UDP 响应网关
一、模块概述 ngx_stream_return_module 提供了一个极简的指令: return <value>;在收到客户端连接后,立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量(如 $time_iso8601、$remote_addr 等)&a…...
突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合
强化学习(Reinforcement Learning, RL)是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程,然后使用强化学习的Actor-Critic机制(中文译作“知行互动”机制),逐步迭代求解…...
无法与IP建立连接,未能下载VSCode服务器
如题,在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈,发现是VSCode版本自动更新惹的祸!!! 在VSCode的帮助->关于这里发现前几天VSCode自动更新了,我的版本号变成了1.100.3 才导致了远程连接出…...
渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止
<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet: https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...
UDP(Echoserver)
网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法:netstat [选项] 功能:查看网络状态 常用选项: n 拒绝显示别名&#…...
前端导出带有合并单元格的列表
// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...
土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等
🔍 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术,可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势,还能有效评价重大生态工程…...
redis和redission的区别
Redis 和 Redisson 是两个密切相关但又本质不同的技术,它们扮演着完全不同的角色: Redis: 内存数据库/数据结构存储 本质: 它是一个开源的、高性能的、基于内存的 键值存储数据库。它也可以将数据持久化到磁盘。 核心功能: 提供丰…...
6️⃣Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙
Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙 一、前言:离区块链还有多远? 区块链听起来可能遥不可及,似乎是只有密码学专家和资深工程师才能涉足的领域。但事实上,构建一个区块链的核心并不复杂,尤其当你已经掌握了一门系统编程语言,比如 Go。 要真正理解区…...
