1. 程式人生 > 實用技巧 >[OHIF-Viewers]醫療數字閱片-醫學影像-Lodash 是一個一致性、模組化、高效能的 JavaScript 實用工具庫。_.throttle(func, [wait=0], [options={}])例項解析防抖動(Debouncing)和節流閥(Throttling)

[OHIF-Viewers]醫療數字閱片-醫學影像-Lodash 是一個一致性、模組化、高效能的 JavaScript 實用工具庫。_.throttle(func, [wait=0], [options={}])例項解析防抖動(Debouncing)和節流閥(Throttling)

[OHIF-Viewers]醫療數字閱片-醫學影像-Lodash 是一個一致性、模組化、高效能的 JavaScript 實用工具庫。_.throttle(func, [wait=0], [options={}])例項解析防抖動(Debouncing)和節流閥(Throttling)

OHIF-Viewers裡面有一句

import throttle from 'lodash.throttle';

先找手冊,這裡貼中文的手冊地址吧,看著方便一點

https://www.lodashjs.com/docs/lodash.throttle

npmjs下載地址

https://www.npmjs.com/package/lodash.throttle

npm i  lodash.throttle

_.throttle(func, [wait=0], [options={}])

建立一個節流函式,在 wait 秒內最多執行func一次的函式。 該函式提供一個cancel方法取消延遲的函式呼叫以及flush方法立即呼叫。 可以提供一個 options 物件決定如何呼叫func方法, options.leading 與|或 options.trailing 決定 wait 前後如何觸發。func會傳入最後一次傳入的引數給這個函式。 隨後呼叫的函式返回是最後一次func呼叫的結果。

注意:如果leadingtrailing都設定為truefunc

允許 trailing 方式呼叫的條件為: 在wait期間多次呼叫。

如果wait0並且leadingfalse,func呼叫將被推遲到下一個點,類似setTimeout0的超時。

引數

  1. func(Function): 要節流的函式。
  2. [wait=0](number): 需要節流的毫秒。
  3. [options={}](Object): 選項物件。
  4. [options.leading=true](boolean): 指定呼叫在節流開始前。
  5. [options.trailing=true](boolean): 指定呼叫在節流結束後。

返回

(Function): 返回節流的函式。

例子

// 避免在滾動時過分的更新定位
jQuery(window).on('scroll', _.throttle(updatePosition, 100)); // 點選後就呼叫 `renewToken`,但5分鐘內超過1次。 var throttled = _.throttle(renewToken, 300000, { 'trailing': false }); jQuery(element).on('click', throttled); // 取消一個 trailing 的節流呼叫。 jQuery(window).on('popstate', throttled.cancel);

例項解析防抖動(Debouncing)和節流閥(Throttling)

原文:Debouncing and Throttling Explained Through Examples

防抖(Debounce)和節流(throttle)都是用來控制某個函式在一定時間內執行多少次的技巧,兩者相似而又不同。

當我們給 DOM 繫結事件的時候,加了防抖和節流的函式變得特別有用。為什麼呢?因為我們在事件和函式執行之間加了一個控制層。記住,我們是無法控制 DOM 事件觸發頻率的。

看下滾動事件的例子:

當使用觸控板,滾動滾輪,或者拖拽滾動條的時候,一秒可以輕鬆觸發30次事件。經我的測試,在智慧手機上,慢慢滾動一下,一秒可以觸發事件100次之多。這麼高的執行頻率,你的滾動回撥函式壓力大嗎?

早在2011年,Twitter 網站丟擲了一個問題:向下滾動 Twitter 資訊流的時候,變得很慢,很遲鈍。John Resig 發表了一篇部落格解釋這個問題,文中解釋到直接給scroll事件關聯昂貴的函式,是多麼糟糕的主意。

John(5年前)建議的解決方案是,在onScroll事件外部,每 250ms 迴圈執行一次。簡單的技巧,避免了影響使用者體驗。

現如今,有一些稍微高階的方式處理事件。我來結合用例介紹下 Debounce,Throttle 和 requestAnimationFrame 吧。

防抖動(Debounce)

防抖技術可以把多個順序地呼叫合併成一次。

假想一下,你在電梯中,門快要關了,突然有人準備上來。電梯並沒有改變樓層,而是再次開啟梯門。電梯延遲了改變樓層的功能,但是優化了資源。

在頂部按鈕上點選或移動滑鼠試一下:

你可以看到連續快速的事件是如何被一個 debounce 事件替代的。但是如果事件觸發的時間間隔過長,debounce 則不會生效。

前緣(或者“immediate”)

你會發現,直到事件停止快速執行以後,debounce 事件才會觸發相應功能。為何不立即觸發呢?那樣的話就跟原本的非 debounce 處理無異了。

直到兩次快速呼叫之間的停頓結束,事件才會再次觸發。

這是帶leading標記的例子:

前緣 debounce 的例子

在 underscore.js 中,選項叫immediate,而不是leading

Debounce 實現

我首次看到 debounce 的 JavaScript 實現是在 2009 年的John Hann 的博文

不久後,Ben Alman 做了個jQuery 外掛(不再維護),一年後 Jeremy Ashkenas 把它加入了 underscore.js。而後加入了 Lodash 。

Lodash 給_.debounce_.throttle添加了不少特性。之前的immediateleading(最前面) 和trailing(最後面) 選項取代。你可以選一種,或者都選,預設只有trailing啟用。

新的maxWait選項(僅 Lodash 有)本文未提及,但是也很有用。事實上,throttle 方法是用_.debouncemaxWait實現的,你可以看lodash 原始碼

Debounce 例項

調整大小的例子

調整桌面瀏覽器視窗大小的時候,會觸發很多次resize事件。

看下面 demo:

如你所見,我們為 resize 事件使用了預設的trailing選項,因為我們只關心使用者停止調整大小後的最終值。

基於 AJAX 請求的自動完成功能,通過 keypress 觸發

為什麼使用者還在輸入的時候,每隔50ms就向伺服器傳送一次 AJAX 請求?_.debounce可以幫忙,當用戶停止輸入的時候,再發送請求。

此處也不需要leading標記,我們想等最後一個字元輸完。

相似的使用場景還有,直到使用者輸完,才驗證輸入的正確性,顯示錯誤資訊。

如何使用 debounce 和 throttle 以及常見的坑

自己造一個 debounce / throttle 的輪子看起來多麼誘人,或者隨便找個博文複製過來。我是建議直接使用 underscore 或 Lodash。如果僅需要_.debounce_.throttle方法,可以使用 Lodash 的自定義構建工具,生成一個 2KB 的壓縮庫。使用以下的簡單命令即可:

npm i -g lodash-cli
lodash-cli include=debounce,throttle

常見的坑是,不止一次地呼叫_.debounce方法:

// 錯誤
$(window).on('scroll', function() {
_.debounce(doSomething, 300);
});
 
// 正確
$(window).on('scroll', _.debounce(doSomething, 200));

debounce 方法儲存到一個變數以後,就可以用它的私有方法debounced_version.cancel(),lodash 和 underscore.js 都有效。

var debounced_version = _.debounce(doSomething, 200);
$(window).on('scroll', debounced_version);
 
// 如果需要的話
debounced_version.cancel();

Throttle(節流閥)

使用 _.throttle 的時候,只允許一個函式在 X 毫秒內執行一次。

跟 debounce 主要的不同在於,throttle 保證 X 毫秒內至少執行一次。

節流閥例項

無限滾動

使用者向下滾動無限滾動頁面,需要檢查滾動位置距底部多遠,如果鄰近底部了,我們可以發 AJAX 請求獲取更多的資料插入到頁面中。

我們心愛的_.debounce就不適用了,只有當用戶停止滾動的時候它才會觸發。只要使用者滾動至鄰近底部時,我們就想獲取內容。

使用_.throttle可以保證我們不斷檢查距離底部有多遠。

requestAnimationFrame(rAF)

requestAnimationFrame是另一種限速執行的方式。

_.throttle(dosomething, 16)等價。它是高保真的,如果追求更好的精確度的話,可以用瀏覽器原生的 API 。

可以使用 rAF API 替換 throttle 方法,考慮一下優缺點:

優點

  • 動畫保持 60fps(每一幀 16 ms),瀏覽器內部決定渲染的最佳時機
  • 簡潔標準的 API,後期維護成本低

缺點

  • 動畫的開始/取消需要開發者自己控制,不像 ‘.debounce’ 或 ‘.throttle’由函式內部處理。
  • 瀏覽器標籤未啟用時,一切都不會執行。
  • 儘管所有的現代瀏覽器都支援 rAF,IE9,Opera Mini 和 老的 Android 還是需要打補丁
  • Node.js 不支援,無法在伺服器端用於檔案系統事件。

根據經驗,如果 JavaScript 方法需要繪製或者直接改變屬性,我會選擇requestAnimationFrame,只要涉及到重新計算元素位置,就可以使用它。

涉及到 AJAX 請求,新增/移除 class (可以觸發 CSS 動畫),我會選擇_.debounce或者_.throttle,可以設定更低的執行頻率(例子中的200ms 換成16ms)。

rAF 例項

靈感來自於Paul Lewis 的文章,我將用 requestAnimationFrame 控制 scroll 。

16ms 的_.throttle拿來做對比,效能相仿,用於更復雜的場景時,rAF 可能效果更佳。

headroom.js 是個更高階的例子

結論

使用 debounce,throttle 和requestAnimationFrame都可以優化事件處理,三者各不相同,又相輔相成。

總之:

  • debounce:把觸發非常頻繁的事件(比如按鍵)合併成一次執行。

  • throttle:保證每 X 毫秒恆定的執行次數,比如每200ms檢查下滾動位置,並觸發 CSS 動畫。

  • requestAnimationFrame:可替代 throttle ,函式需要重新計算和渲染螢幕上的元素時,想保證動畫或變化的平滑性,可以用它。注意:IE9 不支援。