Promise的使用及原理
此文章主要讲解核心思想和基本用法,想要了解更多细节全面的使用方式,请阅读官方API 这篇文章假定你具备最基本的异步编程知识,例如知道什么是回调,知道什么是链式调用,同时具备最基本的单词量,例如page、user、promise、then、resovle、reject、pay、fix、order等等,如果你对这些单词非常陌生,那么你需要先花点时间补充一下你的英语
什么是异步操作?
所谓异步操作,指的是可以跟当前程序同时执行的操作。举例:
$("#page").scrolltop(0 ,1000); //使用1秒钟时间将页面滚动至顶部
$("#nav-float").hide (1000); //使用1秒钟时间将悬浮导航栏隐藏只要你稍微有点异步编程经验,就应该知道,这两个方法会同时完成。
它们的编写顺序并不会影响它们的执行顺序
//异步操作的特点就是,不会打断当前程序的执行//getUsers请求发出后,会立刻向下继续执行第二个请求
ajax("/getUsers",function(data) {//回掉函数会在请求成功后调用
})
//resumelist请求会立刻开始,无论getUsers是否结束
ajax("/resumelist", function(data) {})
//至于哪一个ajax先返回结果并执行回调函数,从代码的编写顺序上是无法确定的。我们可以给异步操作做一个简单的定义
当一个操作开始执行后,主程序无需等待它的完成,可以继续向下执行。此时该操作可以跟主程序同时(并发)执行。这种操作我们就称之为异步操作。 通常当操作完成时,会执行一个我们事先设定好的回调函数来做后续的处理。
我们常见的异步操作例如:
添加定时器 setTimeout/setInterval
执行某个动画 animate
发起网络请求 request
异步会带来什么问题?
比如我们现在有两个动画,需要按顺序来执行,也就是第一个结束,第二个才能开始 这个时候可能有点麻烦,传统的解决方法是通过回调:
animateA(function( ){animateB( );
}) 这种方案显然不太好,如果有很多异步操作需要顺序执行,就会产生所谓的“回调地狱”
ajaxA(function( ){ajaxB(function( ){ajaxC(function( ){ajaxD(function( ){...... }); }); });
}) 这种代码不管是写起来还是读起来都比较烦人。
我们来看下经过Promise改造后的样子(伪代码)
newPromise(ajaxA).then(ajaxB).then(ajaxC).then(ajaxD); Promise的使用及原理
要熟练Promise的的使用,你必须要先搞懂它解决问题的原理 贴一段实际的Promise代码,你来感受一下先:
newPromise(resolve=>{ajax("/pay/post", data=>resolve() );
}).then(resolve=>{ajax("/order/fix", data=>{//处理数据 })
})上面的代码使用了ES6的箭头函数,虽然大大简化了代码的写法,
但对于初级程序猿来讲极不友好
读这种代码简直跟读金刚经差不多。
我们把代码还原成ES5的样子
newPromise(function(resolve){ajax("/pay/post",function(data){resolve();})
}).then(function(){ajax("/order/fix",function(data){})
})接下来,我们就按照费曼技巧来一步步的学习Promise是如何解决问题的
问题1, 作为一个异步函数,尤其像ajax这种网络请求,连我自己都不能确定函数的执行时间,Promise是怎么知道第一个函数什么时候结束的? 然后再开始执行下一个?
Promise并没有那么神奇,它并不能知道我们的函数什么时候结束,
你注意到上面代码中的第3行了吗
在ajax请求结束执行回调的时候,
我们调用了一个resolve()函数,这句代码非常的关键.
这其实就是在通知Promise,当前这个函数结束啦,
你可以开始执行下一个。 这时Promise就会去执行then里面的函数了。问题2, 所以按照你的意思,如果我不调用这个方法,Promise就不知道这个函数有没有结束,那么then里面的函数就不会执行,也就是说我的第二个请求就永远不会发送了呗?
Bingo!! 恭喜你已经学会了逻辑推理+抢答。问题3, 可是这个resolve函数是从哪来的? 需要我自己定义吗? 从代码上看它好像是个参数,那又是谁传入函数中的?
你得先弄明白Promise的基本结构
newPromise(函数1).then(函数2);我们把函数1和函数2都以参数形式传给了一个Promise对象,
所以接下来函数1和2都会由这个Promise对象控制,
简单的说,函数1和函数2都会由Promise对象来执行。
所以在函数1执行时,参数也当然是由Promise对象传递进去的。newPromise(function(resolve){//resolve是Promise对象在调用函数时传入的参数
}).then(函数2);问题4, Promise对象为啥要在执行第1个任务的时候,把这个resolve函数 传进来,有什么目的?
你说呢?废屁,知道还用问你?
真是猪脑子,刚才不是已经说了吗?
Promise对象没办法知道我们的异步函数啥时候结束。
那我来问你, 如果你去车站接人,
可是你又不知道对方何时下车,你会咋办?把我电话号码给他,快到了打我电话呗
没错,Promise解决问题也采用了同样的思路。
它传进来的resolve函数, 就好像一个对讲机,
当我们的异步任务要结束时,通过对讲机 来通知Promise对象。
也就是调用resolve方法newPromise(function(resolve){ajax("/pay/post",function(data){//当请求结束时,通过调用resolve方法,通知Promise对象,该任务已完成resolve(); //收到通知后,Promise会立刻开始函数2的执行})
}).then(函数2);懂了,所以这个resolve函数,必须在异步任务的最后调用(例如ajax的回调方法),相当于告诉Promise对象,该任务结束,请开始下一个。
完全正确问题5, 所以Promise也不过如此嘛,它没有带来什么功能上的革命性变化, 因为使用传统的回调嵌套的方式,同样可以完成效果。 说白了它就是编码方式上的改进??
基本是这样的,但Promise带来的编码方式以及异步编程思路上的进步是非常巨大的。问题6, 那如果我有ajaxA、ajaxB、ajaxC三个异步任务,想按照先A后B再C的顺序执行,像这样写行吗?
newPromise(function(resolve){ajax("/AAA", function(){resolve(); //通知Promise该任务结束})
}).then(function(resolve){ajax("/BBB", function(){resolve();//通知Promise该任务结束})
}).then(function(){ajax("/CCC", function(){ //.... })
}) 上面的这种写法是不对的。
Promise的中文含义是“承诺”,
则意味着,每一个Pormise对象,代表一次承诺
而每一次承诺,只能保证一个任务的顺序,也就是说
newPromise(A).then(B); 这句话表示, 只能保证A和B的顺序一旦A执行完,B开始后,这次承诺也就兑现了,Promise对象也就失效了
那如果还有C呢? 我们就必须在函数B中,
重新创建新的Promise对象,来完成下一个承诺,具体的写法就像这样:newPromise(函数1(resolve){ajaxA("xxxx", function(){resolve();//通知Promise该任务结束})
}).then(函数2(){//在函数2开始运行后,第一次创建的Promise对象完成使命,已经不能再继续工作。//此时,我们创建并返回了新的Promise对象returnnewPromise(function(resolve){ajaxB("xxxx", function(){resolve();//通知新的Promise对象该任务结束}) })
}).then(函数3(){ //尽管这里使用了链式调用,但负责执行函数3的,已经是新的Promise对象了// 如果,我们还有ajaxD需要顺序调用// 那就必须在这里重新new Promise()对象了ajaxC("xxx", function(){ })
}) 问题7, 懂了,那Promise还有什么其它强大的功能吗?
有啊,例如: 如果我有 A,B,C 三个异步任务,ABC同时开始执行
当A,B,C三个任务全部都结束时,执任务D,
传统方法实现起来就比较复杂,Promise就非常简单,就像这样:Promise.all([newPromise(A), newPromise(B), newPromise(C)])
.then(function(){D();
});问题8, 那如果我希望A,B,C 其中任意一个任务完成, 就马上开始任务D,该怎么做?
Promise.race([newPromise(A), newPromise(B), newPromise(C)])
.then(function(){D();
});
相关文章:
Promise的使用及原理
此文章主要讲解核心思想和基本用法,想要了解更多细节全面的使用方式,请阅读官方API 这篇文章假定你具备最基本的异步编程知识,例如知道什么是回调,知道什么是链式调用,同时具备最基本的单词量,例如page、us…...
怎么拥有一个帅气的 CMD 命令窗口 ❓ - Windows
自从拥有这样一个炫酷的命令窗口,我都舍不得关掉它了 关于我为什么我要闲的去 “打扮” 一个命令窗口,这要从星期五下午的一场 摸鱼 🐠 开始,当时我要创建一个 vue ts vite 的项目练练手,为新项目开始做准备&#x…...
时隔多年再学习Vuex,什么?原来如此简单!
时隔多年再学习Vuex,什么?原来如此简单! start 写 Vue 写了好多年了,少不了和 Vuex 打交道。虽然使用它的次数非常频繁,但是潜意识里总觉得这东西很难,导致遇到与之相关的问题就容易慌张。时至今日,升级版…...
Linux笔记_gcc
Linux_gcc程序的翻译链接库make与makefile关于gcc的一些笔记。 程序的翻译 gcc/g是一个编译器。 预处理:头文件展开、条件编译、宏替换、去注释 编译:C语言汇编语言 汇编:汇编->可重定位目标二进制文件,不可以被执行࿰…...
2023美赛MCM A题 详细思路
2023美赛(MCM/ICM)如期开赛,为了尽早的帮大家确定选题。这里我们加急为大家编辑出A赛题详细思路,方便大家快速对A题目的难度有个大致的了解。同时,我们也给出了A题目简要的解题思路,以及该问题在实际解决中可能会遇到的难点。A题的…...
c#: NetTopologySuite凹凸多边形计算
环境: .net 6.0NetTopologySuite 2.5.0vs2022平面二维 一、夹角计算 1.1 计算向量与x轴正方向的夹角 方法: AngleUtility.Angle(Coordinate p) 下图上的t2即为p,之所以这么写是为了和AngleUtility.AngleBetweenOriented做比较 注意: 结果…...
NFT Insider #86:A16z 领投,YGG 获得 1380 万美元融资,The Sandbox与《北斗神拳》合作
引言:NFT Insider由NFT收藏组织WHALE Members、BeepCrypto联合出品,浓缩每周NFT新闻,为大家带来关于NFT最全面、最新鲜、最有价值的讯息。每期周报将从NFT市场数据,艺术新闻类,游戏新闻类,虚拟世界类&#…...
Sort_Algorithm
排序算法前言插入排序折半插入排序希尔排序冒泡排序快速排序选择排序堆排序归并排序前言 排序算法:将一堆数据元素按关键字递增或者递减的顺序,进行排序。 排序算法的评价指标:时间复杂度,空间复杂度,算法稳定性。 算…...
【初探人工智能】2、雏形开始长成
【初探人工智能】2、雏形开始长成【初探人工智能】2、雏形开始长成安装Flask封装Web接口雏形设置接收参数功能验证聊天写代码代码补全生成图片写在后面笔者初次接触人工智能领域,文章中错误的地方还望各位大佬指正! 【初探人工智能】2、雏形开始长成 在…...
【LeetCode】剑指 Offer(2)
目录 写在前面: 题目: 题目的接口: 解题思路: 代码: 过啦!!! 写在最后: 写在前面: 今天的每日一题好难,我不会dp啊啊啊啊啊啊。 所以&am…...
【JavaSE】Lambda、Stream(659~686)
659.每天一考 1.写出获取Class实例的三种常见方式 Class clazz1 String.class; Class clazz2 person.getClass(); //sout(person); //xxx.yyy.zzz.Person... Class clazz3 Class.forName(String classPath);//体现反射的动态性2.谈谈你对Class类的理解 Class实例对应着加载…...
有限差法(Finite Difference)求梯度和Hessian Matrix(海森矩阵)的python实现
数学参考 有限差方法求导,Finite Difference Approximations of Derivatives,是数值计算中常用的求导方法。数学上也比较简单易用。本文主要针对的是向量值函数,也就是f(x):Rn→Rf(x):\mathbb{R^n}\rightarrow \mathbb{R}f(x):Rn→R当然&…...
day33 贪心算法 | 1005、K次取反后最大化的数组和 134、加油站 135、分发糖果
题目 1005、K次取反后最大化的数组和 给定一个整数数组 A,我们只能用以下方法修改该数组:我们选择某个索引 i 并将 A[i] 替换为 -A[i],然后总共重复这个过程 K 次。(我们可以多次选择同一个索引 i。) 以这种方式修改…...
《蓝桥杯每日一题》递推·AcWing 3777. 砖块
1.题目描述n 个砖块排成一排,从左到右编号依次为 1∼n。每个砖块要么是黑色的,要么是白色的。现在你可以进行以下操作若干次(可以是 0 次):选择两个相邻的砖块,反转它们的颜色。(黑变白…...
mysql读写分离(maxscale)
1. 环境架构 需要三台服务器。192.168.2.10(master)192.168.2.20(slave)192.168.2.30(maxscale) 2. 部署mysql主从同步 mysql主从同步可以参考mysql主从同步 3. 部署maxscale服务 MaxScale中间件软件 …...
第八章 - 数据分组( group by , having , select语句顺序)
第八章 - 数据分组 group by数据分组过滤分组 having分组排序groub by语句的一些规定select语句顺序数据分组 在使用group by进行分组时,一般都会配合聚合函数一起使用,实现统计数据的功能。比如下面例子,需要按性别计算人数。按性别进行分组…...
Git(GitHub,Gitee 码云,GitLab)详细讲解
目录第一章 Git 概述1.1 何为版本控制1.2 为什么需要版本控制1.3 版本控制工具1.4 Git 简史1.5 Git 工作机制1.6 Git 和代码托管中心第二章 Git 安装第三章 Git 常用命令3.1 设置用户签名3.2 初始化本地库3.3 查看本地库状态3.3.1 首次查看(工作区没有任何文件&…...
策略模式(Strategy Pattern)
编写鸭子项目,具体要求如下: 1) 有各种鸭子(比如 野鸭、北京鸭,水鸭等,鸭子有各种行为,比如 叫,飞行等) 2)显示鸭子的信息 传统方案解决鸭子问题 1࿰…...
《Qt6开发及实例》6-2 Qt6基础图形的绘制
目录 一、绘图框架设计 二、绘图区的实现 2.1 PaintArea类 2.2 PaintArea类讲解 三、主窗口的实现 3.1 MainWidget类 3.2 MainWidget类讲解 3.3 槽函数编写 3.5 其他内容 一、绘图框架设计 界面 两个类 二、绘图区的实现 2.1 PaintArea类 paintarea.h #ifndef…...
LeetCode 382. 链表随机节点
原题链接 难度:middle\color{orange}{middle}middle 题目描述 给你一个单链表,随机选择链表的一个节点,并返回相应的节点值。每个节点 被选中的概率一样 。 实现 SolutionSolutionSolution 类: Solution(ListNodehead)Solution…...
Python爬虫实战:研究MechanicalSoup库相关技术
一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
ABAP设计模式之---“简单设计原则(Simple Design)”
“Simple Design”(简单设计)是软件开发中的一个重要理念,倡导以最简单的方式实现软件功能,以确保代码清晰易懂、易维护,并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计,遵循“让事情保…...
SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)
上一章用到了V2 的概念,其实 Fiori当中还有 V4,咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务),代理中间件(ui5-middleware-simpleproxy)-CSDN博客…...
Spring是如何解决Bean的循环依赖:三级缓存机制
1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间互相持有对方引用,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...
面向无人机海岸带生态系统监测的语义分割基准数据集
描述:海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而,目前该领域仍面临一个挑战,即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...
【学习笔记】erase 删除顺序迭代器后迭代器失效的解决方案
目录 使用 erase 返回值继续迭代使用索引进行遍历 我们知道类似 vector 的顺序迭代器被删除后,迭代器会失效,因为顺序迭代器在内存中是连续存储的,元素删除后,后续元素会前移。 但一些场景中,我们又需要在执行删除操作…...
深度学习之模型压缩三驾马车:模型剪枝、模型量化、知识蒸馏
一、引言 在深度学习中,我们训练出的神经网络往往非常庞大(比如像 ResNet、YOLOv8、Vision Transformer),虽然精度很高,但“太重”了,运行起来很慢,占用内存大,不适合部署到手机、摄…...
MySQL的pymysql操作
本章是MySQL的最后一章,MySQL到此完结,下一站Hadoop!!! 这章很简单,完整代码在最后,详细讲解之前python课程里面也有,感兴趣的可以往前找一下 一、查询操作 我们需要打开pycharm …...
LLaMA-Factory 微调 Qwen2-VL 进行人脸情感识别(二)
在上一篇文章中,我们详细介绍了如何使用LLaMA-Factory框架对Qwen2-VL大模型进行微调,以实现人脸情感识别的功能。本篇文章将聚焦于微调完成后,如何调用这个模型进行人脸情感识别的具体代码实现,包括详细的步骤和注释。 模型调用步骤 环境准备:确保安装了必要的Python库。…...
