这是Jerry 2021年的第 9 篇文章,也是汪子熙公众号总共第 280 篇原创文章。前端
本文Jerry本来写于2016年5月,当时发布于团队内部wiki. 编程
五年事后,Jerry使用的开发框架,从SAP UI5变成了Angular.浏览器
最近Jerry在作SAP Spartacus开发时,遇到了和本文描述极为相似的场景。由于我学习新知识的时候,总喜欢把以前已经熟悉的知识拿来作横向类比,因此本文首先重温一个很多SAP UI5开发人员都理解得似是而非的知识点,为后续的分享作一个铺垫。缓存
本公众号后续的文章,会介绍如何在Angular技术栈里,使用RxJS优雅地解决此类问题。框架
RxJS是Jerry以前的文章 Jerry在2020 SAP全球技术大会的分享:SAP Spartacus技术介绍的文字版 曾经提到的,一个Angular重度依赖的基于Observables的响应式编程库。异步
本文余下的部分,咱们从新回到SAP UI5的世界。函数
My Opportunities是SAP成都研究院CRM Fiori开发团队于2014年到2016年之间,负责的CRM Fiori应用之一。用户在建立Opportunity时,须要指定Account字段,该字段支持Live Search功能,好比敲入一个字符"J", UI5会发送一个OData请求到后台,后者异步返回Account模型里fullname字段包含J的那些数据,做为搜索结果,经过下拉列表的方式显示在UI上:工具
OData请求url:学习
/sap/opu/odata/sap/CRM_OPPORTUNITY/AccountCollection?$top=10&$filter=substringof(%27J%27,fullName)&sap-client=001&$expand=MainAddress&$select=accountID,MainAddress/city,MainAddress/country,fullName测试
以此类推,若是在字符J后面再敲一个e,会触发一个新的OData请求,根据"Je"搜索:
/sap/opu/odata/sap/CRM_OPPORTUNITY/AccountCollection?$top=10&$filter=substringof(%27Je%27,fullName)&sap-client=001&$expand=MainAddress&$select=accountID,MainAddress/city,MainAddress/country,fullName
当时组内有同事观察到一个现象:若是用户快速输入一连串字符,则在Chrome开发者工具Network标签页里,从时间顺序上来讲,先触发的OData请求,状态会被标注为canceled:
基于这个观察结果,有同事作出了这样的猜想:
极短期内发送两个OData请求,则第一个会自动被cancel掉。
这个猜想即使纯粹从字面意义上讲,也有两点值得推敲之处:
(1) “极短期”,多短算极短?1毫秒?1微秒?
(2) OData请求被谁cancel掉?UI5框架仍是浏览器?
为了验证这个猜想是否正确,我写了一段简单的测试代码:在一个for循环里使用SAP UI5 OData Model read API,发出10个OData请求:
测试发现,不管是同步仍是异步请求,都未出现被cancel的状况。
10个同步请求的执行状况以下图所示:同Timeline序列栏能够看到,10个同步请求按时间顺序,被后台处理,再依次收到响应。
10个异步请求的执行状况:10个请求几乎同时发出,同时收到响应。
测试结果代表,这个猜想“极短期内发送两个OData请求,则第一个会自动被cancel掉”不成立。
可是咱们在Chrome开发者工具里,确实观察到有OData请求被cancel,这又如何解释呢?
首先使用Chrome开发者工具network标签页里的Initiator功能,找到这些被cancel的OData请求,是下图第523行的refresh方法触发的:
在refresh方法内,若是第600行的标志位bChangeDetected为true,那么执行第601行的abortPendingRequest:
因此,这是一个“相煎何太急”的场景:在使用SAP UI5 OData Model API发送OData请求时,若是知足条件(bChangeDetected = true),则OData API会终止(abort)前一个pending的请求。
abortPendingRequest的注释写道:若是开发人员能确信,当前请求的响应数据再也不须要,则可调用该方法来停止该请求。回到本文开头介绍的Opportunity Live Search的例子,当用户输入J,而后再输入e时,显然,前一个根据J进行搜索的请求,已经再也不须要了,咱们仅仅须要将Je对应的请求发送到后台便可。这就是abortPendingRequest方法调用的使用场景之一。
总共有多少种状况,会触发SAP UI5去调用abortPendingRequest方法?
根据关键字abortPendingRequest搜索,获得下列三处位置,即OData Model的三个API方法:
filter
sort
refresh
Jerry以前SAP CRM开发团队的同事Ben(文章 SAP成都研究院李三郎:SCP Application Router简介 的做者),对这三个API作了进一步的研究。
在SAP UI5的OData框架的ODataModel.js中,维护了一个HTTP请求的pending列表,其内维护了已经发送,可是尚未收到响应的request对象:
SAP UI5每次发起OData请求时,都会调用ODataModel的_request()方法。该方法会把当前的request对象加到pending列表中,并经过一个wrap method包装回调函数,确保在响应返回时,首先把缓存的request对象从pending列表中拿掉:
每次使用OData Model API发起filter, sort和refresh操做时,SAP UI5都会检查pending列表中是否存在pending的request对象。若存在,则先abort掉它,这就是咱们在Chrome开发者工具里观察到的状态为canceled的HTTP请求。
总结
只有同时知足下列三个条件,咱们才能观察到SAP UI5发出的OData请求被cancel的状况。
(1) 同一个OData Model实例发出的连续请求,由于pending列表是维护在this级别上的。
(2) 某个请求发送时,存在前一个状态还处于pending的HTTP请求。
(3) SAP UI5应用程序经过OData Model API发起filter, sort或者refresh操做。
这也印证了本文开始Jerry在for循环里,连续调用OData Model的read API发送请求时,没有观察到出现cancel的状况,由于不知足上述条件3.
固然,大前端发展到今天,已经有各类完善的理念和方案来避免此类问题。好比函数节流(throttle)和防抖(debounce)理念:
假设咱们把用户在Account字段的每一次输入事件,触发的事件处理函数的执行,用一根竖线表示。则未经任何处理的原始场景,用函数节流和函数防抖从新实现的场景,三者比较的示意图以下:
从图中不难看出,应用了函数节流和防抖机制后,事件响应函数的触发频次大大下降。当事件响应函数自己包含了复杂耗时的业务逻辑时,触发频次的下降意味着避免了大量没必要要的执行开销。
Jerry后续的文章,会经过实际例子,来介绍SAP UI5如何实现函数节流和防抖,两者的区别,以及如何在Angular里用RxJS更优雅地实现这两种机制。感谢阅读。
更多阅读
(2) SAP UI5 控件渲染机制
(3) HTML原生事件 VS SAP UI5 Semantic事件
(7) SAP UI5控件数据绑定的三种模式:One Way, Two Way和OneTime实现原理比较
(8) SAP UI5控件ID的生成逻辑
(9) SAP UI5控件的多语言(国际化,Internationalization,i18n)支持的实现原理
(10) XML视图里的button控件
(11) button控件和它背后的DOM元素
更多Jerry的原创文章,尽在:"汪子熙":