SAP UI5 OData谣言粉碎机:极短期内发送两个Odata request,前一个会自动被cancel掉吗

这是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更优雅地实现这两种机制。感谢阅读。

更多阅读

(0) SAP UI5应用开发人员了解UI5框架代码的意义

(1) SAP UI5 module懒加载机制

(2) SAP UI5 控件渲染机制

(3) HTML原生事件 VS SAP UI5 Semantic事件

(4) SAP UI5控件元数据的元数据实现

(5) SAP UI5控件的实例数据修改和读取逻辑

(6) SAP UI5控件数据绑定的实现原理

(7) SAP UI5控件数据绑定的三种模式:One Way, Two Way和OneTime实现原理比较

(8) SAP UI5控件ID的生成逻辑

(9) SAP UI5控件的多语言(国际化,Internationalization,i18n)支持的实现原理

(10) XML视图里的button控件

(11) button控件和它背后的DOM元素

更多Jerry的原创文章,尽在:"汪子熙":