Jerry之前的文章 SAP UI5 OData謠言粉碎機:極短時間内發送兩個Odata request, 前一個會自動被cancel掉嗎,介紹過SAP成都研究院CRM Fiori開發團隊開發過的一個Live Search的場景。
使用者建立Opportunity,維護Account字段,每輸入一個字元,都會觸發SAP UI5 Input控件的liveChange事件。在該事件的onAccountInputFieldChanged處理函數裡,根據使用者輸入,發送OData請求到背景進行查詢。

電梯延遲關門這個場景,就是一個典型的函數防抖的現執行個體子。電梯關門的行為就是“函數”,通過電梯門的自動關閉逾時時間,2秒,來延遲電梯門的關閉動作的執行,進而降低電梯門的關閉頻率,這就是“防抖”。
可以想象,如果電梯門的自動關閉沒有設定逾時時間,而是檢測到沒有人進出之後,立即關閉,這樣會大大增加電梯門開合的頻率,既浪費能源,也不安全。這就好比Jerry本文開頭提到的例子:既然我短時間内輸入了字元1234,我期望在UI看到的,是背景服務接收到1234後傳回的結果。至于背景如何對前三個請求,即字元1,字元12和字元123進行處理,我不再關心。
我們可以仿照電梯門關閉逾時時間的設定,來給SAP UI5的函數調用實作防抖控制。
下圖debounce變量是一個函數構造器,本身是一個函數,接收另一個函數fn作為輸入參數,職責是通過閉包,将fn改造成一個具有防抖控制功能的新函數,該新函數通過第17行的return語句傳回。
防抖時間間隔通過函數構造器另一個輸入參數delay指定。
假設我們指定的防抖時間間隔為3000毫秒即3秒,如果3秒之内,debounce函數構造器傳回的新函數被不斷調用,此時執行上圖代碼第19行,調用clearTimeout重置計數器,此時原始函數fn不會得到執行。這個場景可以類比成:在電梯關門逾時時間内,又有新的乘客進入,電梯逾時計時器重置,電梯門不會關閉。
代碼第20行,使用setTimeout重新開機逾時時間間隔為3秒的計數器,3秒過後,如果JavaScript任務隊列裡沒有其他待執行任務,則執行原始函數fn. 代碼的第20行,好比電梯裝置重新開啟了3秒的逾時定時器。
如果在等待的3秒之内,沒有新的函數調用觸發,則3秒過後,執行21行的原始函數fn;這好比電梯在3秒之内,始終沒有新的乘客進入,則 3秒過後,電梯門自動關閉。
debounce函數構造器的使用方式也很簡單。
代碼第78行,将原始的sendRequest函數,以及3000毫秒的防抖時間間隔,傳入debounce構造器,傳回一個兼有資料發送功能和防抖功能的debounceVersion函數。在第85行原來調用sendRequest函數的位置,改為調用debounceVersion函數即可。
函數防抖功能的測試:我在同一分鐘的第46秒,48秒,50秒,51秒四個時間點,分别輸入了1,2,3,4總共4個字元,但是在最後一次即51.996秒又過了3秒之後,才僅僅有一個請求發送到背景:這說明3秒的函數防抖間隔生效了:
SAP UI5如何使用函數節流(Throttle)來降低函數調用的頻次
上述函數防抖的實作存在一個問題,還是以電梯的例子來說明。
設想有一個空間無限的電梯,關門的逾時時間為3秒。如果不斷的有新的乘客以小于3秒的時間間隔進入電梯,則電梯門永遠沒有機會關閉——即函數永遠得不到執行。
函數節流(Throttle)是另一種降低函數調用頻次的思路,同函數防抖的差別是,後者能保證在指定的節流間隔内,至少執行一次函數。
函數節流構造器的一個最簡單的實作版本:
被節流器改造後的函數每次觸發時,取一個目前系統時間戳,同前一次觸發時取的時間戳比較。如果二者的時間差,大于等于構造器的輸入參數delay即節流時間間隔,則進入第39行的else分支,觸發原始函數fn;否則說明節流時間間隔還未到達,使用第34行setTimeout,将原始函數fn,重新放入JavaScript事件隊列内,延遲執行:
函數節流版本的構造器使用方式,同函數防抖版本的構造器沒有差别:将原始函數sendRequest傳入構造器throttle,傳回一個具有節流功能的新函數throttleVersion,在Input控件liveChange事件處理函數裡,調用throttleVersion這個新函數即可。
本文介紹的兩種函數防抖和函數節流的實作代碼,僅僅考慮了最基本的情況,還有很多不完善的地方,有興趣的朋友可以在網絡上搜尋,這方面的資料非常多,這裡不再贅述。
Jerry之前的分享提到過,Angular是響應式程式設計開發庫RxJS的重度使用者,後者提供了衆多功能強大的Operators,使得Angular開發人員不用重複造輪子,就能輕易實作出具有函數防抖和函數節流的場景。
從rxjs/operators工具庫中直接導出debounceTime和throttleTime這兩個operators: