写点什么

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

作者:Jerry Wang
  • 2023-06-21
    上海
  • 本文字数:2313 字

    阅读完需:约 8 分钟

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

最近 Jerry 在做 SAP Spartacus 开发时,遇到了和本文描述极为类似的场景。因为我学习新知识的时候,总喜欢把之前已经熟悉的知识拿来做横向类比,所以本文首先重温一个不少 SAP UI5 开发人员都理解得似是而非的知识点,为后续的分享做一个铺垫。


笔者后续的文章,会介绍如何在 Angular 技术栈里,使用 RxJS 优雅地解决此类问题。


RxJS 是一个 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 方法:


filtersortrefresh



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



发布于: 刚刚阅读数: 3
用户头像

Jerry Wang

关注

🏆InfoQ写作平台-签约作者🏆 2017-12-03 加入

SAP成都研究院开发专家,SAP社区导师,SAP中国技术大使。2007 年从电子科技大学计算机专业硕士毕业后加入 SAP 成都研究院工作至今。工作中使用 ABAP, Java, JavaScript 和 TypeScript 进行开发。

评论

发布
暂无评论
SAP UI5 OData 谣言粉碎机:极短时间内发送两个 Odata 请求,前一个会自动被 cancel 掉吗_SAP_Jerry Wang_InfoQ写作社区