19-简单理解JavaScript中的Promise:手写Promise实现
笔记+分享
在现代JavaScript开发中,异步编程是不可避免的一部分。为了更好地处理异步操作,ES6引入了Promise。Promise使得异步代码更具可读性和可维护性。通过手写一个完整的Promise实现,可以帮助你更深入地理解其工作原理。本文将详细介绍Promise的概念、用法及其在实际开发中的应用,并提供一个手写的Promise实现。
什么是Promise?
Promise是JavaScript中的一种对象,用于表示一个异步操作的最终完成(或失败)及其结果值。它有三种状态:
- Pending(待定):初始状态,既没有被兑现,也没有被拒绝。
- Fulfilled(已兑现):操作成功完成,并返回一个值。
- Rejected(已拒绝):操作失败,并返回一个原因。
手写Promise实现
我们将通过构建一个简化版的Promise实现来更好地理解其工作原理。
class MyPromise {constructor(executor) {this.state = 'pending'; // 初始状态this.value = undefined; // 成功时的值this.reason = undefined; // 失败时的原因this.onFulfilledCallbacks = []; // 成功的回调函数数组this.onRejectedCallbacks = []; // 失败的回调函数数组const resolve = (value) => {if (this.state === 'pending') {this.state = 'fulfilled';this.value = value;this.onFulfilledCallbacks.forEach(fn => fn());}};const reject = (reason) => {if (this.state === 'pending') {this.state = 'rejected';this.reason = reason;this.onRejectedCallbacks.forEach(fn => fn());}};try {executor(resolve, reject);} catch (error) {reject(error);}}then(onFulfilled, onRejected) {onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason };let promise2 = new MyPromise((resolve, reject) => {if (this.state === 'fulfilled') {setTimeout(() => {try {let x = onFulfilled(this.value);resolvePromise(promise2, x, resolve, reject);} catch (error) {reject(error);}}, 0);}if (this.state === 'rejected') {setTimeout(() => {try {let x = onRejected(this.reason);resolvePromise(promise2, x, resolve, reject);} catch (error) {reject(error);}}, 0);}if (this.state === 'pending') {this.onFulfilledCallbacks.push(() => {setTimeout(() => {try {let x = onFulfilled(this.value);resolvePromise(promise2, x, resolve, reject);} catch (error) {reject(error);}}, 0);});this.onRejectedCallbacks.push(() => {setTimeout(() => {try {let x = onRejected(this.reason);resolvePromise(promise2, x, resolve, reject);} catch (error) {reject(error);}}, 0);});}});return promise2;}catch(onRejected) {return this.then(null, onRejected);}static resolve(value) {return new MyPromise((resolve, reject) => {resolve(value);});}static reject(reason) {return new MyPromise((resolve, reject) => {reject(reason);});}static all(promises) {return new MyPromise((resolve, reject) => {let results = [];let completed = 0;const processResult = (index, value) => {results[index] = value;if (++completed === promises.length) {resolve(results);}};promises.forEach((promise, index) => {MyPromise.resolve(promise).then(value => {processResult(index, value);}, reject);});});}static race(promises) {return new MyPromise((resolve, reject) => {promises.forEach(promise => {MyPromise.resolve(promise).then(resolve, reject);});});}
}function resolvePromise(promise2, x, resolve, reject) {if (promise2 === x) {return reject(new TypeError('Chaining cycle detected for promise'));}let called = false;if (x && (typeof x === 'object' || typeof x === 'function')) {try {let then = x.then;if (typeof then === 'function') {then.call(x, y => {if (called) return;called = true;resolvePromise(promise2, y, resolve, reject);}, reason => {if (called) return;called = true;reject(reason);});} else {resolve(x);}} catch (error) {if (called) return;called = true;reject(error);}} else {resolve(x);}
}
关键点解释
- 状态管理:Promise有三种状态:
pending、fulfilled和rejected。通过state变量来管理当前Promise的状态。 - 回调队列:使用两个数组
onFulfilledCallbacks和onRejectedCallbacks来存储在pending状态时注册的回调函数。 - resolve和reject:
resolve函数将Promise状态从pending变为fulfilled,并执行所有成功回调;reject函数将Promise状态从pending变为rejected,并执行所有失败回调。 - then方法:
then方法返回一个新的Promise,并根据当前Promise的状态决定如何处理回调函数。 - resolvePromise函数:这个函数用来处理
then方法中的链式调用,确保Promise的规范执行,防止循环引用等问题。
用法示例
const p1 = new MyPromise((resolve, reject) => {setTimeout(() => {resolve('成功');}, 1000);
});p1.then(value => {console.log(value); // 输出 "成功"return new MyPromise((resolve, reject) => {setTimeout(() => {resolve('链式调用成功');}, 1000);});
}).then(value => {console.log(value); // 输出 "链式调用成功"
}).catch(error => {console.error(error);
});
通过这个手写的Promise实现,你可以更好地理解Promise的内部工作机制,并在实际开发中灵活运用Promise来处理异步操作。希望这篇博客能帮助你深入理解JavaScript中的Promise。
相关文章:
19-简单理解JavaScript中的Promise:手写Promise实现
笔记分享 在现代JavaScript开发中,异步编程是不可避免的一部分。为了更好地处理异步操作,ES6引入了Promise。Promise使得异步代码更具可读性和可维护性。通过手写一个完整的Promise实现,可以帮助你更深入地理解其工作原理。本文将详细介绍Pr…...
elementUI input 禁止内容两端存在空格,或者是自动去除两端空格
需求 项目中有需求:输入框中禁止内容两端存在空格,或者是自动去除两端空格。 解决方法 vue的api文档中有过介绍,使用.trim可以去掉用户输入内容中两端的空格,如下图 代码 <el-input v-model.trim"name" cleara…...
Go语言24小时极速学习教程(一)基础语法
Go语言(也称为Golang)是一种由Google开发的编程语言,以其简洁、高效和并发支持而闻名。从本文开始,将带你快速完成Go语言的学习,如果你之前有过Java或者C语言的基础,学习它将很容易,本教程忽略环…...
LLMs之Code:Qwen2.5-Coder的简介、安装和使用方法、案例应用之详细攻略
LLMs之Code:Qwen2.5-Coder的简介、安装和使用方法、案例应用之详细攻略 导读:这篇论文介绍了Qwen2.5-Coder系列模型,这是一个针对代码生成的强大开源大型语言模型。 >> 背景痛点:现有代码大型语言模型的不足:虽然…...
pytest结合allure做接口自动化
这是一个采用pytest框架,结合allure完成接口自动化测试的项目,最后采用allure生成直观美观的测试报告,由于添加了allure的特性,使得测试报告覆盖的内容更全面和阅读起来更方便。 1. 使用pytest构建测试框架,首先配置好…...
TypeScript简介:TypeScript是JavaScript的一个超集
官方描述:TypeScript 是 JavaScript 的一个超集 GitHub官网:https://github.com/Microsoft/TypeScript TypeScript is a superset of JavaScript that compiles to clean JavaScript output. TypeScript 是 JavaScript 的一个超集,支持 EC…...
【循环测试试题2】小X与三次方
题目描述 卡卡西要过 10 岁生日啦!今年,她特别想要一份与众不同的礼物,那就是一条能在阳光下发出五光十色耀眼光芒的水晶项链。她把这个想法告诉了妈妈。妈妈对卡卡西神秘的一笑,透露道:“邻居芭比阿姨家有个后花园。…...
【Python · PyTorch】卷积神经网络(基础概念)
【Python PyTorch】卷积神经网络 CNN(基础概念) 0. 生物学相似性1. 概念1.1 定义1.2 优势1.2.1 权重共享1.2.2 局部连接1.2.3 层次结构 1.3 结构1.4 数据预处理1.4.1 标签编码① One-Hot编码 / 独热编码② Word Embedding / 词嵌入 1.4.2 归一化① Min-…...
深入描述dts和dtsi的区别
QA:dts和dtsi的区别 在嵌入式 Linux 系统中,DTS(Device Tree Source)和 DTSI(Device Tree Source Include)是描述硬件设备树的文件格式。它们本质上是同一种语法的文件,但在使用上有一定区别。…...
京准时钟:一种北斗卫星校时器的结构设计
京准时钟:一种北斗卫星校时器的结构设计 京准时钟:一种北斗卫星校时器的结构设计 1、有关时间的一些基本概念: 时间与频率之间互为倒数关系,两者密不可分,时间标准的基础是频率标准,由晶体振荡器决定时间…...
【WiFi】ubuntu20.4 WiFi6 无线抓包环境搭建及使用
环境说明 笔记本电脑,无线网卡AX200,安装ubuntu20.04 安装无线网卡工具aircrack-ng sudo apt-get install aircrack-ng 安装wireshark sudo add-apt-repository ppa:wireshark-dev/stable sudo apt update sudo apt -y install wireshark sudo apt -…...
《Java核心技术 卷I》用户界面AWT事件继承层次
AWT事件继承层次 EventObject类有一个子类AWTEvent,它是所有AWT事件类的父类。 Swing组件会生成更多其他事件对象,都直接拓展自EventObject而不是AWTEvent。 AWT将事件分为底层(low-level)事件和语义事件。 语义事件:表示用户的动作事件&…...
蓝牙 HFP 协议详解及 Android 实现
文章目录 前言一、什么是蓝牙 HFP 协议?HFP 的核心功能HFP 的核心功能HFP 在 Android 中的典型应用场景 二、HFP 协议的工作流程HFP 的连接流程 三、HFP 在 Android 的实现1. 检查蓝牙适配器状态2. 发现并检测支持 HFP 的设备3. 获取 BluetoothHeadset 服务4. 连接设…...
sqli-labs靶场17-20关(每日四关)持续更新!!!
Less-17 打开靶场,发现页面比之前多了一行字 翻译过来就是,密码重置,大家肯定会想到,自己平时在日常生活中怎么密码重置,肯定是输入自己的用户名,输入旧密码,输入新密码就可以了,但…...
动态规划-完全背包问题——518.零钱兑换II
1.题目解析 建议先看 322.零钱兑换可以 更加轻松的理解本题 题目来源 518.零钱兑换——力扣 测试用例 2.算法原理 1.状态表示 本题要求返回所有情况,所以dp值就代表所有的方法数,即 dp[i][j]:在[1,i]个硬币中选择不同面值的硬币,…...
[模板总结] - 单向链表LinkedList操作
题目汇总 Leetcode 21, 82, 160, 206, 237, 268 Leetcode 21. 合并两个有序链表 归并排序的思路,创建一个哨兵节点从两个链表中按大小插入即可。 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode(…...
fastadmin多个表crud连表操作步骤
1、crud命令 php think crud -t xq_user_credential -u 1 -c credential -i voucher_type,nickname,user_id,voucher_url,status,time --forcetrue2、修改控制器controller文件 <?phpnamespace app\admin\controller;use app\common\controller\Backend;/*** 凭证信息…...
山西省网络建设与运维第十八届职业院校技能大赛(样题)
集团计划把部分业务由原有的 X86 架构服务器 上迁移到 ARM 架构服务器上,同时根据目前的部分业务需求进行了部分 调整和优化。 一、 X86 架构计算机安装与管理 1、PC1系统为 ubuntu-desktop-amd64 系统,登录用户为 xiao,密码为 Key-1122。在对…...
服务端高并发分布式结构进阶之路
序言 在技术求知的旅途中,鉴于多数读者缺乏在中大型系统实践中的亲身体验,难以从宏观角度把握某些概念,因此,本文特选取“电子商务应用”作为实例,详细阐述从百级至千万级并发场景下服务端架构的逐步演变历程。同时&am…...
分布式微服务项目,同一个controller不同方法间的转发导致cookie丢失,报错null pointer异常
源码: /***添加商品进入购物车*/ GetMapping("/addToCart") public String addToCart(RequestParam("num") Integer num, RequestParam("skuId") Long skuId, RedirectAttributes redirectAttributes) {System.out.println("nu…...
未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?
编辑:陈萍萍的公主一点人工一点智能 未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战,在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...
【Oracle APEX开发小技巧12】
有如下需求: 有一个问题反馈页面,要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据,方便管理员及时处理反馈。 我的方法:直接将逻辑写在SQL中,这样可以直接在页面展示 完整代码: SELECTSF.FE…...
Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...
DBAPI如何优雅的获取单条数据
API如何优雅的获取单条数据 案例一 对于查询类API,查询的是单条数据,比如根据主键ID查询用户信息,sql如下: select id, name, age from user where id #{id}API默认返回的数据格式是多条的,如下: {&qu…...
今日科技热点速览
🔥 今日科技热点速览 🎮 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售,主打更强图形性能与沉浸式体验,支持多模态交互,受到全球玩家热捧 。 🤖 人工智能持续突破 DeepSeek-R1&…...
【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统
目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索(基于物理空间 广播范围)2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
上位机开发过程中的设计模式体会(1):工厂方法模式、单例模式和生成器模式
简介 在我的 QT/C 开发工作中,合理运用设计模式极大地提高了代码的可维护性和可扩展性。本文将分享我在实际项目中应用的三种创造型模式:工厂方法模式、单例模式和生成器模式。 1. 工厂模式 (Factory Pattern) 应用场景 在我的 QT 项目中曾经有一个需…...
32位寻址与64位寻址
32位寻址与64位寻址 32位寻址是什么? 32位寻址是指计算机的CPU、内存或总线系统使用32位二进制数来标识和访问内存中的存储单元(地址),其核心含义与能力如下: 1. 核心定义 地址位宽:CPU或内存控制器用32位…...
【Ftrace 专栏】Ftrace 参考博文
ftrace、perf、bcc、bpftrace、ply、simple_perf的使用Ftrace 基本用法Linux 利用 ftrace 分析内核调用如何利用ftrace精确跟踪特定进程调度信息使用 ftrace 进行追踪延迟Linux-培训笔记-ftracehttps://www.kernel.org/doc/html/v4.18/trace/events.htmlhttps://blog.csdn.net/…...
