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的原創文章,盡在:"汪子熙":