当前位置: 首页 > news >正文

手写 Promise(1)核心功能的实现

一:什么是 Promise

        Promise 是异步编程的一种解决方案,其实是一个构造函数,自己身上有all、reject、resolve这几个方法,原型上有then、catch等方法。    

Promise对象有以下两个特点。

(1)对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。

(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

二:手写Promise

1、手写构造函数

设计思路

  • 定义类
  • 添加构造函数
  • 定义resolve/reject
  • 执行回调函数

代码实现 

    <script>// 创建一个类class wePromise{// 实现构造器,并且实现resolve和reject两个方法constructor(func) {const reslove = (result) => {console.log('reslove-执行啦:',result);}const reject = (result) => {console.log('reject-执行啦:',result);}// 执行回调函数func(reslove,reject)}}// 创建对象,调用两个方法const p = new wePromise((reslove,reject) => {console.log('执行');reslove('success')reject('reject')})</script>

运行效果

2、手写状态及原因

设计思路

  • 添加状态
  • 添加原因
  • 调整resolve/reject
  • 状态不可逆

代码实现

    <script>// 使用变量保存状态,便于后续使用,Ctrl + shift + pconst PENDING = 'pending'const FULFILLED = 'fulfilled'const REJECTED = 'rejected'class wePromise {state = PENDING // 状态result = undefined // 原因// 实现构造器,并且实现resolve和reject两个方法constructor(func) {// 改状态,pending => fulfilled// 记录原因const reslove = (result) => {if (this.state === PENDING) { // 锁定状态this.state = FULFILLEDthis.result = result}}// 改状态,pending => rejected// 记录原因const reject = (result) => {if (this.state === PENDING) { // 锁定状态this.state = REJECTEDthis.result = result}}// 执行回调函数func(reslove, reject)}}// 创建对象,调用两个方法const p = new wePromise((reslove, reject) => {reslove('success')reject('reject')})</script>

运行效果

3、then方法--成功和失败的回调

设计思路

  • 添加实例方法
  • 参数判断
  • 执行成功/失败回调

代码实现

    <script>const PENDING = 'pending'const FULFILLED = 'fulfilled'const REJECTED = 'rejected'class wePromise {state = PENDING // 状态result = undefined // 原因// 构造函数constructor(func) {// 改状态,pending => fulfilledconst reslove = (result) => {if (this.state === PENDING) {this.state = FULFILLEDthis.result = result}}// 改状态,pending => rejectedconst reject = (result) => {if (this.state === PENDING) {this.state = REJECTEDthis.result = result}}// 执行回调函数func(reslove, reject)}then(onFulfilled,onReject){// 判断传入的参数是不是函数onFulfilled = typeof onFulfilled === 'function'?onFulfilled : x=>xonReject = typeof onReject === 'function'?onReject : x=>{throw x}// 判断执行完成后的状态if(this.state === FULFILLED){onFulfilled(this.result) // 返回结果}else if(this.state === REJECTED){onReject(this.result)}}}// 创建对象,调用两个方法const p = new wePromise((reslove, reject) => {reslove('success')// reject('reject')})p.then(res => {console.log('成功回调:',res);},err => {console.log('失败回调:',err);})</script>

运行效果

4、then方法--异步和多次调用

设计思路

  • 定义实例属性
  • 保存回调函数
  • 调用成功/失败回调函数

代码实现

    <script>const PENDING = 'pending'const FULFILLED = 'fulfilled'const REJECTED = 'rejected'class wePromise {state = PENDING // 状态result = undefined // 原因#handlers = [] // [{onFulfilled,onReject},......]// 构造函数constructor(func) {// 改状态,pending => fulfilledconst reslove = (result) => {if (this.state === PENDING) {this.state = FULFILLEDthis.result = result// 下面这个是异步的时候,先保存,然后到这一步执行,就取出保存的函数并且执行this.#handlers.forEach(({ onFulfilled })=>{ // 解构onFulfilled(this.result)})}}// 改状态,pending => rejectedconst reject = (result) => {if (this.state === PENDING) {this.state = REJECTEDthis.result = resultthis.#handlers.forEach(({ onReject })=>{onReject(this.result)})}}// 执行回调函数func(reslove, reject)}then(onFulfilled, onReject) {// 判断传入的参数是不是函数onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : x => xonReject = typeof onReject === 'function' ? onReject : x => { throw x }// 判断执行完成后的状态if (this.state === FULFILLED) {onFulfilled(this.result) // 返回结果} else if (this.state === REJECTED) {onReject(this.result)} else if (this.state === PENDING){ // 还没有改变状态,说明是异步// 保存回调函数this.#handlers.push({onFulfilled,onReject})}}}// 创建对象,调用两个方法const p = new wePromise((reslove, reject) => {setTimeout(() => {reslove('success')// reject('reject')}, 2000)})p.then(res => {console.log('成功回调1:', res);}, err => {console.log('失败回调1:', err);})p.then(res => {console.log('成功回调2:', res);}, err => {console.log('失败回调2:', err);})</script>

运行效果

5、异步任务

api介绍:

  • 使用api:queueMicrotask、MutationObserve、setTimeout
  • queueMicrotask:内置的全局函数,直接queueMicrotask() 就可以调用
  • MutationObserve:内置的全局函数,比较麻烦,需要创建节点,具体看代码
  • setTimeout:内置的全局函数,定时器

设计思路

  • 定义函数
  • 调用核心api
  • 调用函数

代码实现

    <script>// 定义函数function runAsynctask(callback) {// callback 是一个回调函数// 调用核心apiif (typeof queueMicrotask === 'function') { // 调用三个函数是为了解决浏览器不兼容问题,先判断是不是函数queueMicrotask(callback)} else if (typeof MutationObserver === "function") {const obs = new MutationObserver(callback)const divNode = document.createElement('div')obs.observe(divNode, { childList: true })divNode.innerHTML = '打酱油改变以下内容'} else {setTimeout(callback, 0)}}const PENDING = 'pending'const FULFILLED = 'fulfilled'const REJECTED = 'rejected'class wePromise {state = PENDING // 状态result = undefined // 原因#handlers = [] // [{onFulfilled,onReject},......]// 构造函数constructor(func) {// 改状态,pending => fulfilledconst reslove = (result) => {if (this.state === PENDING) {this.state = FULFILLEDthis.result = result// 下面这个是异步的时候,先保存,然后到这一步执行,就取出保存的函数并且执行this.#handlers.forEach(({ onFulfilled }) => { // 解构onFulfilled(this.result)})}}// 改状态,pending => rejectedconst reject = (result) => {if (this.state === PENDING) {this.state = REJECTEDthis.result = resultthis.#handlers.forEach(({ onReject }) => {onReject(this.result)})}}// 执行回调函数func(reslove, reject)}then(onFulfilled, onReject) {// 判断传入的参数是不是函数onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : x => xonReject = typeof onReject === 'function' ? onReject : x => { throw x }// 判断执行完成后的状态if (this.state === FULFILLED) {runAsynctask(() => {onFulfilled(this.result) // 返回结果})} else if (this.state === REJECTED) {runAsynctask(() => {onReject(this.result)})} else if (this.state === PENDING) { // 还没有改变状态,说明是异步// 保存回调函数this.#handlers.push({onFulfilled: () => {runAsynctask(() => {onFulfilled(this.result) // 返回结果})},onReject: () => {runAsynctask(() => {onReject(this.result)})}})}}}console.log('top');// 创建对象,调用两个方法const p = new wePromise((reslove, reject) => {reslove('success')// reject('reject')})p.then(res => {console.log('成功回调:', res);}, err => {console.log('失败回调:', err);})console.log('bottom');</script>

运行效果

6、链式编程

设计思路

  • 返回Promise实例
  • 获取返回值
  • 处理返回值
    • 处理异常
    • 处理返回Promise(调用then方法)
    • 处理重复引用(抛出异常)

代码实现

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>7.处理返回Promise</title>
</head><body><script>// 定义函数function runAsynctask(callback) {// callback 是一个回调函数// 调用核心apiif (typeof queueMicrotask === 'function') { // 调用三个函数是为了解决浏览器不兼容问题,先判断是不是函数queueMicrotask(callback)} else if (typeof MutationObserver === "function") {const obs = new MutationObserver(callback)const divNode = document.createElement('div')obs.observe(divNode, { childList: true })divNode.innerHTML = '打酱油改变以下内容'} else {setTimeout(callback, 0)}}const PENDING = 'pending'const FULFILLED = 'fulfilled'const REJECTED = 'rejected'class wePromise {state = PENDING // 状态result = undefined // 原因#handlers = [] // [{onFulfilled,onReject},......]// 构造函数constructor(func) {// 改状态,pending => fulfilledconst reslove = (result) => {if (this.state === PENDING) {this.state = FULFILLEDthis.result = result// 下面这个是异步的时候,先保存,然后到这一步执行,就取出保存的函数并且执行this.#handlers.forEach(({ onFulfilled }) => { // 解构onFulfilled(this.result)})}}// 改状态,pending => rejectedconst reject = (result) => {if (this.state === PENDING) {this.state = REJECTEDthis.result = resultthis.#handlers.forEach(({ onReject }) => {onReject(this.result)})}}// 执行回调函数func(reslove, reject)}then(onFulfilled, onReject) {// 判断传入的参数是不是函数onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : x => xonReject = typeof onReject === 'function' ? onReject : x => { throw x }const p2 = new wePromise((reslove, reject) => {// 判断执行完成后的状态if (this.state === FULFILLED) {runAsynctask(() => {try {// 获取返回值const x = onFulfilled(this.result) // 返回结果reslovePromise(p2, x, reslove, reject)} catch (error) {// 处理异常reject(error)}})} else if (this.state === REJECTED) {runAsynctask(() => {try {const x = onReject(this.result)reslovePromise(p2, x, reslove, reject)} catch (error) {reject(error)}})} else if (this.state === PENDING) { // 还没有改变状态,说明是异步// 保存回调函数this.#handlers.push({onFulfilled: () => {runAsynctask(() => {try {const x = onFulfilled(this.result) // 返回结果reslovePromise(p2, x, reslove, reject)} catch (error) {reject(error)}})},onReject: () => {runAsynctask(() => {try {const x = onReject(this.result) // 返回结果reslovePromise(p2, x, reslove, reject)} catch (error) {reject(error)}})}})}})return p2}}function reslovePromise(p2, x, reslove, reject) {// 处理重复引用if (x === p2) {// 抛出错误throw new TypeError('Chaining cycle detected for promise #<Promise>')}// 处理返回的Promiseif (x instanceof wePromise) {// 调用then方法x.then(res => reslove(res), err => reject(err))} else {// 处理返回值reslove(x)}}// 创建对象,调用两个方法const p = new wePromise((reslove, reject) => {reslove('success')// reject('reject')})p.then(res => {return new wePromise((reslove, reject) => {reslove(2)})// console.log('成功回调1:', res);// throw 'throw-error'// return 2}, err => {console.log('失败回调1:', err);}).then(res => {console.log('成功回调2:', res);}, err => {console.log('失败回调2:', err);})</script>
</body></html>

运行效果

        这个运行效果是只测了一个,有兴趣的小伙伴可以吧代码复制下来自己测。

三:总结

        截止到目前为止已经将 Promise 的核心功能实现了,还有实例方法和静态方法这些没有实现。由于篇幅过长,因此会拆分成两篇文章来完成,感觉本文还不错的小伙伴可以继续看下一篇哦!

相关文章:

手写 Promise(1)核心功能的实现

一&#xff1a;什么是 Promise Promise 是异步编程的一种解决方案&#xff0c;其实是一个构造函数&#xff0c;自己身上有all、reject、resolve这几个方法&#xff0c;原型上有then、catch等方法。 Promise对象有以下两个特点。 &#xff08;1&#xff09;对象的状态不受…...

深入探究Java内存模型

文章目录 &#x1f31f; Java虚拟机内存模型&#x1f34a; 一、方法区&#x1f34a; 二、堆&#x1f389; 堆的基本概念&#x1f389; 堆的结构&#x1f4dd; 新生代&#x1f4dd; 老年代 &#x1f389; 堆的分配策略&#x1f4dd; 对象优先分配&#x1f4dd; 空间优先分配 &am…...

深度学习 | Pytorch深度学习实践 (Chapter 10、11 CNN)

十、CNN 卷积神经网络 基础篇 首先引入 —— 二维卷积&#xff1a;卷积层保留原空间信息关键&#xff1a;判断输入输出的维度大小特征提取&#xff1a;卷积层、下采样分类器&#xff1a;全连接 引例&#xff1a;RGB图像&#xff08;栅格图像&#xff09; 首先&#xff0c;老师…...

谈谈你对spring boot 3.0的理解

谈谈你对spring boot 3.0的理解 一&#xff0c;Spring Boot 3.0 的兼容性 Spring Boot 3.0 在兼容性方面做出了很大的努力&#xff0c;以支持存量项目和老项目。尽管如此&#xff0c;仍需注意以下几点&#xff1a; Java 版本要求&#xff1a;Spring Boot 3.0 要求使用 Java 1…...

【大数据】Hadoop

文章目录 概述Hadoop组成HDFSMapReduce写MapReduce程序&#xff08;Hadoop streaming&#xff09; YARNHadoop 启动 工作方式Hadoop的主从工作方式Hadoop的守护进程 运行模式本地运行模式伪分布式运行模式完全分布式运行模式 Hadoop高可用的解决方案ZooKeeper quorumZKFC 环境搭…...

Spring实例化源码解析之Bean的实例化(十二)

前言 本章开始分析finishBeanFactoryInitialization(beanFactory)方法&#xff0c;直译过来就是完成Bean工厂的初始化&#xff0c;这中间就是非lazy单例Bean的实例化流程。ConversionService在第十章已经提前分析了。重点就是最后一句&#xff0c;我们的bean实例化分析就从这里…...

git常用的几条命令介绍

必须了解的命令整理 1&#xff0c;git init 初始化一个新的Git仓库。 这将在当前目录中创建一个名为".git"的子目录&#xff0c;Git会将所有仓库的元数据存储在其中。 2&#xff0c;git clone 克隆一个已存在的仓库。 这会创建一个本地仓库的副本&#xff0c;包…...

使用VisualSVN在Windows系统上设置SVN服务器,并结合内网穿透实现公网访问

文章目录 前言1. VisualSVN安装与配置2. VisualSVN Server管理界面配置3. 安装cpolar内网穿透3.1 注册账号3.2 下载cpolar客户端3.3 登录cpolar web ui管理界面3.4 创建公网地址 4. 固定公网地址访问 前言 SVN 是 subversion 的缩写&#xff0c;是一个开放源代码的版本控制系统…...

第18章 SpringCloud生态(三)

18.21 Nacos能存储什么样格式的数据(配置中心) 难度:★ 重点:★ 白话解析 看下面这副Nacos控制台的截图就明白了 参考答案 六种格式数据:Text、JSON、XML、Yaml、HTML和Properties格式。 18.22 Nacos是如何实现配置动态更新的(配置中心) 难度:★★ 重点:★★★ 白话…...

leetcode:2347. 最好的扑克手牌(python3解法)

难度&#xff1a;简单 给你一个整数数组 ranks 和一个字符数组 suit 。你有 5 张扑克牌&#xff0c;第 i 张牌大小为 ranks[i] &#xff0c;花色为 suits[i] 。 下述是从好到坏你可能持有的 手牌类型 &#xff1a; "Flush"&#xff1a;同花&#xff0c;五张相同花色的…...

2007-2022 年上市公司国内外专利授权情况数据

2007-2022 年上市公司国内外专利授权情况 1、来源&#xff1a;国家知识产权局 2、时间&#xff1a;2007-2022 年 3、范围&#xff1a;上市公司 4、指标&#xff1a; 证券代码、年份、省份、城市、行业代码、授权地区、申请类型、专利、发明专利、实用新型、外观设计 5、…...

安全渗透测试网络基础知识之路由技术

#1.静态路由技术 ##1.1路由技术种类: 静态路由技术、动态路由技术 ##1.2静态路由原理 静态路由是网络中一种手动配置的路由方式,用于指定数据包在网络中的传输路径。与动态路由协议不同,静态路由需要管理员手动配置路由表,指定目的网络和下一跳路由器的关联关系。 比较适合…...

【大数据】Kafka 实战教程(二)

Kafka 实战教程&#xff08;二&#xff09; 1.下载2.安装3.配置4.运行4.1 启动 Zookeeper4.2 启动 Kafka 5.第一个消息5.1 创建一个 Topic5.2 创建一个消息消费者5.3 创建一个消息生产者 1.下载 你可以在 Kafka 官网&#xff1a;http://kafka.apache.org/downloads&#xff0c…...

React 框架

1、React 框架简介 1.1、介绍 CS 与 BS结合&#xff1a;像 React&#xff0c;Vue 此类框架&#xff0c;转移了部分服务器的功能到客户端。将CS 和 BS 加以结合。客户端只用请求一次服务器&#xff0c;服务器就将所有js代码返回给客户端&#xff0c;所有交互类操作都不再依赖服…...

数据结构与算法之图: Leetcode 133. 克隆图 (Typescript版)

克隆图 https://leetcode.cn/problems/clone-graph/description/ 描述 给你无向 连通 图中一个节点的引用&#xff0c;请你返回该图的 深拷贝&#xff08;克隆&#xff09;。 图中的每个节点都包含它的值 val&#xff08;int&#xff09; 和其邻居的列表&#xff08;list[No…...

illuminate/database 使用 一

illuminate/database 是完整的php数据库工具包&#xff0c;即ORM&#xff08;Object-Relational Mapping&#xff09;类库。 提供丰富的查询构造器&#xff0c;和多个驱动的服务。作为Laravel的数据库层使用&#xff0c;也可以单独使用。 一 使用 加载composer之后&#xff…...

前端koa搭建服务器(保姆级教程)——part1

目录 koa简介前端项目搭建koa环境第一步&#xff1a;新建项目第二步&#xff1a;环境初始化&#xff0c;安装依赖初始化项目&#xff0c;生成package.json文件安装koa依赖安装koa-router 路由管理依赖安装dotenv 环境变量依赖安装nodemon 热启动依赖 第三步&#xff1a;代码调用…...

js逆向第一课 密码学介绍

什么是密码学&#xff1f; 密码学&#xff08;Cryptology&#xff09;是一种用来混淆的技术,它希望将正常的、可识别的信息转变为无法识别的信息。 目前密码学的研究&#xff0c;一种是偏应用&#xff0c;把现有的&#xff0c;别人研究出来的密码学算法&#xff0c;放在一个合…...

Dynamic DataSource 多数据源配置【 Springboot + DataSource + MyBatis Plus + Druid】

一、前言 MybatisPlus多数据源配置主要解决的是多数据库连接和切换的问题。在一些大型应用中&#xff0c;由于数据量的增长或者业务模块的增多&#xff0c;可能需要访问多个数据库。这时&#xff0c;就需要配置多个数据源。 二、Springboot MyBatis Plus 数据源配置 2.1、单数…...

MyBatis:配置文件

MyBatis 前言全局配置文件映射配置文件注 前言 在 MyBatis 中&#xff0c;配置文件分为 全局配置文件&#xff08;核心配置文件&#xff09; 和 映射配置文件 。通过这两个配置文件&#xff0c;MyBatis 可以根据需要动态地生成 SQL 语句并执行&#xff0c;同时将结果集转换成 …...

Python|GIF 解析与构建(5):手搓截屏和帧率控制

目录 Python&#xff5c;GIF 解析与构建&#xff08;5&#xff09;&#xff1a;手搓截屏和帧率控制 一、引言 二、技术实现&#xff1a;手搓截屏模块 2.1 核心原理 2.2 代码解析&#xff1a;ScreenshotData类 2.2.1 截图函数&#xff1a;capture_screen 三、技术实现&…...

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...

对WWDC 2025 Keynote 内容的预测

借助我们以往对苹果公司发展路径的深入研究经验&#xff0c;以及大语言模型的分析能力&#xff0c;我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际&#xff0c;我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测&#xff0c;聊作存档。等到明…...

Nginx server_name 配置说明

Nginx 是一个高性能的反向代理和负载均衡服务器&#xff0c;其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机&#xff08;Virtual Host&#xff09;。 1. 简介 Nginx 使用 server_name 指令来确定…...

Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级

在互联网的快速发展中&#xff0c;高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司&#xff0c;近期做出了一个重大技术决策&#xff1a;弃用长期使用的 Nginx&#xff0c;转而采用其内部开发…...

AI编程--插件对比分析:CodeRider、GitHub Copilot及其他

AI编程插件对比分析&#xff1a;CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展&#xff0c;AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者&#xff0c;分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)

骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术&#xff0c;它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton)&#xff1a;由层级结构的骨头组成&#xff0c;类似于人体骨骼蒙皮 (Mesh Skinning)&#xff1a;将模型网格顶点绑定到骨骼上&#xff0c;使骨骼移动…...

Unit 1 深度强化学习简介

Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库&#xff0c;例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体&#xff0c;比如 SnowballFight、Huggy the Do…...

3403. 从盒子中找出字典序最大的字符串 I

3403. 从盒子中找出字典序最大的字符串 I 题目链接&#xff1a;3403. 从盒子中找出字典序最大的字符串 I 代码如下&#xff1a; class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...

JAVA后端开发——多租户

数据隔离是多租户系统中的核心概念&#xff0c;确保一个租户&#xff08;在这个系统中可能是一个公司或一个独立的客户&#xff09;的数据对其他租户是不可见的。在 RuoYi 框架&#xff08;您当前项目所使用的基础框架&#xff09;中&#xff0c;这通常是通过在数据表中增加一个…...