对 Promise 的理解

Promise 是异步编程的一种解决方案,它是一个对象,可以获取异步 操作的消息,他的出现大大改善了异步编程的困境,避免了地狱回调, 它比传统的解决方案回调函数和事件更合理和更强大。

所谓 Promise,简单说就是一个容器,里面保存着某个未来才会结束 的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一 个对象,从它可以获取异步操作的消息。Promise 提供统一的 API, 各种异步操作都可以用同样的方法进行处理。

  1. Promise 的实例有三个状态:
  • Pending(进行中)

  • Resolved(已完成)

  • Rejected(已拒绝)

当把一件事情交给 promise 时,它的状态就是 Pending,任务完成了 状态就变成了 Resolved、没有完成失败了就变成了 Rejected。

  1. Promise 的实例有两个过程:
    pending -> fulfilled: Resolved(已完成)
    pending -> rejected: Rejected(已拒绝)

    注意:一旦从进行状态变成为其他状态就永远不能更改状态了。

Promise 的特点

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

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

Promise 实现

Promise 实现是通过js class编写,主要包括status、value、error、resolve、reject、then、catch、all、race、allSettled、any等组成。

  1. 状态
  const PROMISE_STATUE_PENDING = "pending"; // 进行中const PROMISE_STATUE_FULFILLED = "fulfilled"; // 已完成const PROMISE_STATUE_REJECTED = "rejected"; // 已拒绝
  1. resolve 创建一个已解决的Promise对象,将给定的值作为其参数。
  resolve = (value) => {if (this.statue === PROMISE_STATUE_PENDING) {queueMicrotask(() => {if (this.statue !== PROMISE_STATUE_PENDING) return;this.statue = PROMISE_STATUE_FULFILLED;this.value = value;this.resFns?.forEach((fn) => {fn(this.value);});});}};
  1. reject 创建一个已拒绝的Promise对象,将给定的原因作为其参数
  reject = (error) => {if (this.statue === PROMISE_STATUE_PENDING) {queueMicrotask(() => {if (this.statue !== PROMISE_STATUE_PENDING) return;this.statue = PROMISE_STATUE_REJECTED;this.error = error;this.errFns.forEach((en) => {en(this.error);});});}};
  1. then 添加对Promise对象解决或拒绝时的处理程序
  • 单个方法调用
 constructor(executer) {this.statue = PROMISE_STATUE_PENDING;this.value = void 0;this.error = void 0;this.resFn;this.errFn;const resolve = ((value) => {if (this.status === PROMISE_STATUS_PENDING) {this.status = PROMISE_STATUS_FULFILLEDqueueMicrotask(() => { //queueMicrotask:  主线程执行完毕之后立马执行this.resfn(value)})}})const reject = ((error) => {if (this.status === PROMISE_STATUS_PENDING) {this.status = PROMISE_STATUS_REJECTEDqueueMicrotask(() => {this.errfn(error)})}})executer(this.resolve, this.reject);}then(resFn, errFn) {this.resFn = resFn;this.errFn = errFn;}
  • 执行结果
const p1 = new myPromise((resolve, reject) => {resolve(111)reject(333333)
})
p1.then(res => {        //最终打印 1111console.log(res);
}, err => {console.log(err);})
  • 优化then 方法

官方给与的then 方法是可以进行数组传值和链式调用的,而目前我们写的是不支持。

  this.resFns = [] //1.多次调用then 时用数组 保存this.errFns = []// 将then 方法修改为 then(resFn, errFn) {this.resfns.push(resFn);this.errFns.push(errFn);}// resolve修改为resolve = (value) => {if (this.statue === PROMISE_STATUE_PENDING) {queueMicrotask(() => {if (this.statue !== PROMISE_STATUE_PENDING) return;this.statue = PROMISE_STATUE_FULFILLED;this.value = value;this.resFns?.forEach((fn) => {fn(this.value);});});}};// reject 修改为reject = (error) => {if (this.statue === PROMISE_STATUE_PENDING) {queueMicrotask(() => {if (this.statue !== PROMISE_STATUE_PENDING) return;this.statue = PROMISE_STATUE_REJECTED;this.error = error;this.errFns.forEach((en) => {en(this.error);});});}};

优化后then的运行结果

p1.then(res => {console.log("res1:", res) 
}, err => {console.log("err1:", err)
})
// 调用then方法多次调用
p1.then(res => {console.log("res2:", res)
}, err => {console.log("err2:", err)
})

运行结果:res2: 111 因为后面的.then 把前面的覆盖掉了 并不会执行res1 所在的代码块
*由此可见 then 方法调用时应该是个数组然后依次调用
下面改造我们的代码then,还需要优化执行resolve 时调用reject

then(resFn, errFn) {const defaultOnRejected = (err) => {throw err;};errFn = errFn || defaultOnRejected;const defaultOnFulFilled = (value) => {return value;};resFn = resFn || defaultOnFulFilled;return new MyPromise((resolve, reject) => {if (this.statue === PROMISE_STATUE_FULFILLED && !!resFn) {try {const value = resFn(this.value);resolve(value);} catch (error) {reject(error);}}if (this.statue === PROMISE_STATUE_REJECTED && !!errFn) {try {resolve(value);} catch (error) {reject(error);}}if (this.statue === PROMISE_STATUE_PENDING) {if (!!resFn) {this.resFns.push(() => {try {const value = resFn(this.value);resolve(value);} catch (error) {reject(error);}});}if (!!errFn) {this.errFns.push(() => {try {const value = errFn(this.error);resolve(value);} catch (error) {reject(error);}});}}});}

然后执行:

  const p1 = new myPromise((resolve, reject) => {resolve(111);reject(333333);})p1.then(res => {console.log("res1:", res);}, err => {console.log("err1:", err);})// 调用then方法多次调用p1.then(res => {console.log("res2:", res);}, err => {console.log("err2:", err);})执行结果:res1: 111res2: 111
  1. catch 添加对Promise对象拒绝时的处理程序
  // 添加对Promise对象拒绝时的处理程序。catch(errFn) {return this.then(undefined, errFn);}
  1. finally 添加对Promise对象解决或拒绝时的最终处理程序,无论Promise对象是否已被解决或拒绝。
  finally(fn) {setTimeout(() => {fn();}, 0);}
  1. all 接收一个可迭代对象(如数组),并返回一个新的Promise对象。当所有Promise对象都已解决时,该Promise对象才将被解决,并返回一个包含所有解决值的数组。
  // 通过类型判断当前数组中的方法或者对象是否为Promise 对象const isPromise = function(promise) {return (!!promise &&(typeof promise === "object" || typeof promise === "function") &&typeof promise.then === "function");};/*** 接收一个可迭代对象(如数组),并返回一个新的Promise对象。* 当所有Promise对象都已解决时,该Promise对象才将被解决,并返回一个包含所有解决值的数组。* @param {any[]} iterable* @desc 实际上多个对象同步执行时,就相当于把所有的方法重新进行Promise一次。* 当遍历到最后一个时,resolve 所有结果。* */MyPromise.all = function(iterable) {if (!(iterable instanceof Array)) {return console.log("传入参数必须是一个数组");}return new MyPromise((resolve, reject) => {let len = iterable.length;let count = 0;let results = new Array(len);for (let i = 0; i < len; i++) {let promise = iterable[i];count++;if (isPromise(promise)) {promise.then((res) => {results[i] = res;if (count === len) {resolve(results);}}).catch((err) => {reject(err);});} else if (typeof promise === "function") {results[i] = promise();} else {results[i] = promise;}}// 当数据的所有项都不是promise实例,我们就在这判断多一次,然后resolveif (count === len) {resolve(results);}});};

all 运行示例

  (async function() {const res = MyPromise.all([new MyPromise((resolve) => {resolve(1);}),new MyPromise((resolve) => {resolve(2);}),() => {return 123;},88888,]);res.then((res) => {console.log(res);});})();运行结果: [1, 2, 123,  88888]
  1. race Promise.race(iterable) 传入多个对象,当任何一个执行完成后 resolve 结果
MyPromise.race = function(iterable) {if (!(iterable instanceof Array)) {return console.log("传入参数必须是一个数组");}return new MyPromise((resolve, reject) => {iterable.forEach((p) => {if (isPromise(p)) {p.then((value) => {resolve(value);}).catch((err) => {reject(err);});} else if (typeof p === "function") {resolve(p());} else {resolve(p);}});});
};

race 运行示例

  (async function() {const res = MyPromise.race([new MyPromise((resolve) => {resolve(1);}),new MyPromise((resolve) => {resolve(2);}),]);res.then((res) => {console.log(res);});})();运行结果: 1

完整代码

  // statusconst PROMISE_STATUE_PENDING = "pending"; // 进行中const PROMISE_STATUE_FULFILLED = "fulfilled"; // 已完成const PROMISE_STATUE_REJECTED = "rejected"; // 已拒绝class MyPromise {constructor(executer) {this.statue = PROMISE_STATUE_PENDING;this.value = void 0;this.error = void 0;this.resFns = [];this.errFns = [];executer(this.resolve, this.reject);}//    创建一个已解决的Promise对象,将给定的值作为其参数。resolve = (value) => {if (this.statue === PROMISE_STATUE_PENDING) {queueMicrotask(() => {if (this.statue !== PROMISE_STATUE_PENDING) return;this.statue = PROMISE_STATUE_FULFILLED;this.value = value;this.resFns?.forEach((fn) => {fn(this.value);});});}};// 创建一个已拒绝的Promise对象,将给定的原因作为其参数。reject = (error) => {if (this.statue === PROMISE_STATUE_PENDING) {queueMicrotask(() => {if (this.statue !== PROMISE_STATUE_PENDING) return;this.statue = PROMISE_STATUE_REJECTED;this.error = error;this.errFns.forEach((en) => {en(this.error);});});}};//   添加对Promise对象解决或拒绝时的处理程序。then(resFn, errFn) {const defaultOnRejected = (err) => {throw err;};errFn = errFn || defaultOnRejected;const defaultOnFulFilled = (value) => {return value;};resFn = resFn || defaultOnFulFilled;return new MyPromise((resolve, reject) => {if (this.statue === PROMISE_STATUE_FULFILLED && !!resFn) {try {const value = resFn(this.value);resolve(value);} catch (error) {reject(error);}}if (this.statue === PROMISE_STATUE_REJECTED && !!errFn) {try {resolve(value);} catch (error) {reject(error);}}if (this.statue === PROMISE_STATUE_PENDING) {if (!!resFn) {this.resFns.push(() => {try {const value = resFn(this.value);resolve(value);} catch (error) {reject(error);}});}if (!!errFn) {this.errFns.push(() => {try {const value = errFn(this.error);resolve(value);} catch (error) {reject(error);}});}}});}// 添加对Promise对象拒绝时的处理程序。catch(errFn) {return this.then(undefined, errFn);}// 添加对Promise对象解决或拒绝时的最终处理程序,无论Promise对象是否已被解决或拒绝。finally(fn) {setTimeout(() => {fn();}, 0);}}const isPromise = function(promise) {return (!!promise &&(typeof promise === "object" || typeof promise === "function") &&typeof promise.then === "function");};/*** 接收一个可迭代对象(如数组),并返回一个新的Promise对象。* 当所有Promise对象都已解决时,该Promise对象才将被解决,并返回一个包含所有解决值的数组。* @param {any[]} iterable*/MyPromise.all = function(iterable) {if (!(iterable instanceof Array)) {return console.log("传入参数必须是一个数组");}return new MyPromise((resolve, reject) => {let len = iterable.length;let count = 0;let results = new Array(len);for (let i = 0; i < len; i++) {let promise = iterable[i];count++;if (isPromise(promise)) {promise.then((res) => {results[i] = res;if (count === len) {resolve(results);}}).catch((err) => {reject(err);});} else if (typeof promise === "function") {results[i] = promise();} else {results[i] = promise;}}// 当数据的所有项都不是promise实例,我们就在这判断多一次,然后resolveif (count === len) {resolve(results);}});};MyPromise.race = function(iterable) {if (!(iterable instanceof Array)) {return console.log("传入参数必须是一个数组");}return new MyPromise((resolve, reject) => {iterable.forEach((p) => {if (isPromise(p)) {p.then((value) => {resolve(value);}).catch((err) => {reject(err);});} else if (typeof p === "function") {resolve(p());} else {resolve(p);}});});};// const p1 = new MyPromise((resolve, reject) => {//   console.log("状态pending");//   resolve("22222");//   reject("3333333");// });// p1.then((res) => {//   console.log("res1:", res);//   return "第二次的成功回调";// })//   .catch((error) => {//     console.log("err1:", error);//     throw new Error("第二次的失败回调");//   })//   .finally(() => {//     console.log("finally");//   });// (async function() {//   const res = MyPromise.all([//     new MyPromise((resolve) => {//       resolve(1);//     }),//     new MyPromise((resolve) => {//       resolve(2);//     }),//     () => {//       return 123;//     },//     88888,//   ]);//   res.then((res) => {//     console.log(res);//   });// })();// (async function() {//   const res = MyPromise.race([//     new MyPromise((resolve) => {//       resolve(1);//     }),//     new MyPromise((resolve) => {//       resolve(2);//     }),//   ]);//   res.then((res) => {//     console.log(res);//   });// })();
Promise 的缺点

无法取消 Promise,一旦新建它就会立即执行,无法中途取消。

如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。

当处于 pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始 还是即将完成)。

总结:

Promise 对象是异步编程的一种解决方案,最早由社区提出。Promise 是一个构造函数,接收一个函数作为参数,返回一个 Promise 实例。 一个 Promise 实例有三种状态,分别是 pending、resolved 和 rejected,分别代表了进行中、已成功和已失败。实例的状态只能由 pending 转变 resolved 或者 rejected 状态,并且状态一经改变, 就凝固了,无法再被改变了。

状态的改变是通过 resolve() 和 reject() 函数来实现的,可以在 异步操作结束后调用这两个函数改变 Promise 实例的状态,它的原 型上定义了一个 then 方法,使用这个 then 方法可以为两个状态的 改变注册回调函数。这个回调函数属于微任务,会在本轮事件循环的 末尾执行。

注意:在构造 Promise 的时候,构造函数内部的代码是立即执行的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/28141.shtml

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

相关文章

算法通关村——透彻理解二分查找

1. 循环法 public static int binarySearch(int[] arr, int low, int high, int target) {while (low < high) {// 这样写主要是避免溢出的情况&#xff0c;以及>>优先级小于&#xff0c;避免出现死循环int mid low ((high - low) >> 1);if (arr[mid] target…

7.1 动手实现AlexNet

AlexNet引入了dropput层 代码 import torch from torch import nn from d2l import torch as d2lnet nn.Sequential(# 样本数为1,通道数为96,11x11的卷积核,步幅为4&#xff0c;减少输出的高度和深度。 LeNet的通道数才6&#xff0c;此处96&#xff0c;为什么要增加这么多通…

MIT 6.830数据库系统 -- lab six

MIT 6.830数据库系统 -- lab six 项目拉取引言steal/no-force策略redo log与undo log日志格式和检查点 开始回滚练习1&#xff1a;LogFile.rollback() 恢复练习2&#xff1a;LogFile.recover() 测试结果疑问点分析 项目拉取 原项目使用ant进行项目构建&#xff0c;我已经更改为…

在python中使用nvidia的VPF库对RTSP流进行硬解码并使用opencv进行显示

解码并处理视频流的多线程应用 随着视频处理技术的不断发展&#xff0c;越来越多的应用需要对视频流进行解码和处理。在本文中&#xff0c;我们将介绍一个基于Python的多线程应用程序&#xff0c;该应用程序可以解码并处理多个RTSP视频流&#xff0c;同时利用GPU加速&#xff0…

use gnustep objective-c

first app #import <Foundation/Foundation.h>int main(int argc, const char * argv[]) {NSAutoreleasePool *pool [NSAutoreleasePool new];NSLog("first start");[pool drain];return 0; }tech 专注于概念&#xff0c;而不是迷失在语言技术细节中编程语言…

微服务技术栈(1.0)

微服务技术栈 认识微服务 单体架构 单体架构&#xff1a;将业务的所有功能集中在一个项目中开发&#xff0c;打成一个包部署 优点&#xff1a; 架构简单部署成本低 缺点&#xff1a; 耦合度高 分布式架构 分布式架构&#xff1a;根据业务功能对系统进行拆分&#xff0c…

npm四种下载方式的区别

npm install moduleName 命令 安装模块到项目node_modules目录下。 不会将模块依赖写入devDependencies或dependencies 节点。 运行 npm install 初始化项目时不会下载模块。npm install -g moduleName 命令 安装模块到全局&#xff0c;不会在项目node_modules目录中保存模块包…

如何在 Spring Boot 中集成日志框架 SLF4J、Log4j

文章目录 具体步骤附录 笔者的操作环境&#xff1a; Spring Cloud Alibaba&#xff1a;2022.0.0.0-RC2 Spring Cloud&#xff1a;2022.0.0 Spring Boot&#xff1a;3.0.2 Nacos 2.2.3 Maven 3.8.3 JDK 17.0.7 IntelliJ IDEA 2022.3.1 (Ultimate Edition) 具体步骤 因为 …

Java课题笔记~ 使用 Spring 的事务注解管理事务(掌握)

通过Transactional 注解方式&#xff0c;可将事务织入到相应 public 方法中&#xff0c;实现事务管理。 Transactional 的所有可选属性如下所示&#xff1a; propagation&#xff1a;用于设置事务传播属性。该属性类型为 Propagation 枚举&#xff0c; 默认值为 Propagation.R…

【学习日记】【FreeRTOS】链表结构体及函数详解

写在前面 本文主要是对于 FreeRTOS 中链表相关内容的详细解释&#xff0c;代码大部分参考了野火FreeRTOS教程配套源码&#xff0c;作了一小部分修改。 一、结构体定义 主要包含三种结构体&#xff1a; 普通节点结构体结尾节点&#xff08;mini节点&#xff09;结构体链表结…

awk经典实战、正则表达式

目录 1.筛选给定时间范围内的日志 2.统计独立IP 案列 需求 代码 运行结果 3.根据某字段去重 案例 运行结果 4.正则表达式 1&#xff09;认识正则 2&#xff09;匹配字符 3&#xff09;匹配次数 4&#xff09;位置锚定&#xff1a;定位出现的位置 5&#xff09;分组…

ESP32 Max30102 (3)修复心率误差

1. 运行效果 2. 新建修复心率误差.py 代码如下: from machine import sleep, SoftI2C, Pin, Timer from utime import ticks_diff, ticks_us from max30102 import MAX30102, MAX30105_PULSE_AMP_MEDIUM from hrcalc import calc_hr_and_spo2BEATS = 0 # 存储心率 FINGER_F…

如何识别手机是否有灵动岛(dynamic island)

如何识别手机是否有灵动岛&#xff08;dynamic island&#xff09; 灵动岛是苹果2022年9月推出的iPhone 14 Pro、iPhone 14 Pro Max首次出现&#xff0c;操作系统最低是iOS16.0。带灵动岛的手机在竖屏时顶部工具栏大于等于51像素。 #define isHaveDynamicIsland ({ BOOL isH…

Taro保存图片到手机

萌新亚历山大啊&#xff0c;搞了一下午&#xff0c;真多坑 Taro.downloadFile({url: res,filePath: Taro.env.USER_DATA_PATH /xcxcode.jpg,success: res > {if (res.statusCode 200) {console.log(res)const tempFilePath res.filePath; // 获取下载的临时文件路径// …

编程(Leetcode, SQL)知识

有志者&#xff0c;事竟成 1. Nim游戏 2. sql中求连续时间的问题 思路&#xff1a;一般来说&#xff0c;有两种方向。一种如果是日期的格式&#xff0c;求连续时间可以先提取出日期中的月份或者天&#xff0c;然后减去row_number()生成的rank&#xff0c;以此来计算分组或者不…

微信小程序的项目解构

视频链接 黑马程序员前端微信小程序开发教程&#xff0c;微信小程序从基础到发布全流程_企业级商城实战(含uni-app项目多端部署)_哔哩哔哩_bilibili 接口文档 https://www.escook.cn/docs-uni-shop/mds/1.start.html 1&#xff1a;微信小程序宿主环境 1&#xff1a;常见的宿…

如何创建Google test shared library(dll)和static library(lib),并编写测试用例

从Github下载google test源码确保本地安装Visual Studio和CMake GUI&#xff0c;本次测试使用VS2017及Cmake GUI 3.20.5解压googletest-main&#xff0c;打开Cmake GUI&#xff0c;配置源码路径&#xff08;googletest-main路径&#xff09;&#xff0c;和生成路径&#xff08;…

安达发制造工业迈向智能化:APS高级计划排程助力提升生产效率

随着市场竞争的加剧&#xff0c;制造企业纷纷寻求提高生产效率和降低成本的方法。近年来&#xff0c;越来越多的制造企业开始采用APS(高级计划与排程)系统&#xff0c;以优化生产计划和排程&#xff0c;提高生产效率&#xff0c;并在竞争中取得优势。 现代制造业通常面临复杂的…

【第一阶段】kotlin的range表达式

range:范围&#xff1a;从哪里到哪里的意思 in:表示在 !in&#xff1a;表示不在 … :表示range表达式 代码示例&#xff1a; fun main() {var num:Int20if(num in 0..9){println("差劲")}else if(num in 10..59){println("不及格")}else if(num in 60..89…

【深入了解pytorch】PyTorch强化学习:强化学习的基本概念、马尔可夫决策过程(MDP)和常见的强化学习算法

【深入了解pytorch】PyTorch强化学习:强化学习的基本概念、马尔可夫决策过程(MDP)和常见的强化学习算法 PyTorch强化学习:介绍强化学习的基本概念、马尔可夫决策过程(MDP)和常见的强化学习算法引言强化学习的基本概念状态(State)动作(Action)奖励(Reward)策略(Pol…