SAP UI5和Angular的函数防抖(Debounce)和函数节流(Throttle)实现原理介绍
這是Jerry 2021年的第 11 篇文章,也是汪子熙公眾號總共第 282 篇原創文章。
Jerry之前的文章 SAP UI5 OData謠言粉碎機:極短時間內發送兩個Odata request, 前一個會自動被cancel掉嗎,介紹過SAP成都研究院CRM Fiori開發團隊開發過的一個Live Search的場景。
用戶創建Opportunity,維護Account字段,每輸入一個字符,都會觸發SAP UI5 Input控件的liveChange事件。在該事件的onAccountInputFieldChanged處理函數里,根據用戶輸入,發送OData請求到后臺進行查詢。
如果用戶輸入速度很快,則在短時間內,會有多個OData請求發送到后臺,進而出現Jerry文章里描述的OData請求被cancel的情況。
最近Jerry做SAP Spartacus開發,遇到了同樣的場景。因此通過本文把自己最近所學總結一下,記錄下SAP UI5和Angular里如何使用函數防抖(Debounce)和函數節流(Throttle)來避免短時間內觸發高頻次函數調用的情況出現。
為了便于講解,Jerry做了一個只包含一個Input控件的SAP UI5頁面。源代碼地址.
在Input里輸入字符,會觸發liveChange事件,將當前Input的最新內容,發送到一個我自己開發的后臺服務去。該后臺服務什么也不做,只是簡單將收到的內容返回給UI.
這個SAP UI5頁面里的Input控件的liveChange事件處理如下:
從Chrome控制臺打印的輸出來看,我在一秒鐘之內,連續快速輸入了1234共4個字符,一共產生了4個發送往后臺的請求。
SAP UI5如何使用函數防抖(Debounce)來降低函數調用的頻次
函數防抖(Debounce),最早源于機械開關和繼電器的術語“去彈跳”,即將多個信號合并為一個信號。
想象一個大家現實生活中都會遇到的場景:進電梯。電梯都有一個自動關閉門的超時時間,假設為2秒。當電梯檢測到有人進入時,會重置這個2秒的計時器。如果下一個2秒之內,沒有新的乘客進入電梯,電梯門才會自動關上。
電梯延遲關門這個場景,就是一個典型的函數防抖的現實例子。電梯關門的行為就是“函數”,通過電梯門的自動關閉超時時間,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這個新函數即可。
函數節流的測試結果:我設置的節流時間間隔為3秒,從Chrome控制臺打印輸出能觀察到,SAP UI5確實是大致以3秒的時間間隔,向后臺發起的數據請求。
本文介紹的兩種函數防抖和函數節流的實現代碼,僅僅考慮了最基本的情況,還有很多不完善的地方,有興趣的朋友可以在網絡上搜索,這方面的資料非常多,這里不再贅述。
Jerry之前的分享提到過,Angular是響應式編程開發庫RxJS的重度使用者,后者提供了眾多功能強大的Operators,使得Angular開發人員不用重復造輪子,就能輕易實現出具有函數防抖和函數節流的場景。
用Angular重新實現本文SAP UI5的Demo,總共代碼只有44行:
從rxjs/operators工具庫中直接導出debounceTime和throttleTime這兩個operators:
類似SAP UI5 Input控件的liveChange,Angular FormControl的valueChanges也給應用開發人員提供了編寫業務邏輯,響應用戶輸入的位置:后者的valueChanges數據類型是Observable,應用開發人員可以通過pipe調用,傳入RxJS各種功能強大的Operators,讓自己編寫的包含業務邏輯的事件響應函數,按照實際需求來觸發。
比如上圖第39行代碼,語義是:綁定到jerryFormControl的input控件有valueChanges發生時,首先經過防抖器的處理。至于是否能夠滿足觸發valueChanges對應的事件處理函數的條件,由防抖器debounceTime的內部處理邏輯決定。
RxJS防抖器debounceTime的內部實現使用了setInterval,邏輯比Jerry本文介紹的debounce函數構造器復雜得多了,通過這些調用棧就能感受一二:
Jerry這個Angular Demo的函數節流(時間間隔設定為2秒)功能測試如下:我在7秒之內,勻速輸入1234567890abc,可以看到總共觸發了三個發送到后臺的請求,請求間隔為2秒:
希望本文能幫助大家對函數防抖和函數節流的概念有一個最粗淺的理解,感謝閱讀。
更多閱讀
(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元素
(12) SAP UI5 OData謠言粉碎機:極短時間內發送兩個Odata request,前一個會自動被cancel掉嗎
(13) 漫談SAP產品里頁面上的Checkbox設計與實現系列之一
更多Jerry的原創文章,盡在:“汪子熙”:
總結
以上是生活随笔為你收集整理的SAP UI5和Angular的函数防抖(Debounce)和函数节流(Throttle)实现原理介绍的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 初学者学编程应该先学什么语言(先学什么语
- 下一篇: 公交乘车码可以刷2次吗