iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 29
6
Software Development

前端工程師用 javaScript 學演算法系列 第 29

[有趣面試題] 網頁效能問題改善之 Debounce & Throttle

其實最後這一系列 “聊聊美國找工作以及面試經驗”,本來預計寫個 3 - 4 個有趣題目、至少 3 篇面試準備/異國工作心得,殊不知光資料結構跟演算法就佔用了28 天了?。只能各挪一篇給他們。等之後 30 天結束再不定期寫一下面試前需要準備的事好了。


我過去是在專案公司上班,要馬使用者不多、要馬後台資料量不大,所以我在調效能方面總是很不上心 (完全不會為了快 0.01 秒多花好幾天時間去寫。專案公司根本也不會給你太多時間時做)。所以當第一次看到有 Debounce & Throttle 跟這種概念覺得非常聰明啊 !!! 聽說也是大公司面試很常考的題目之一

適用情境

只要是使用者會在短時間內頻繁觸發多次事件處理器,例如 autocomplete、滑鼠移動、改變視窗大小都可以適用此篇。因為你的瀏覽器會不斷重新計算造成頁面很緩慢
https://ithelp.ithome.com.tw/upload/images/20190930/20106426sZqbVZU44W.png
在開始之前可以看一下這個網站感受不同方法的區別。

以 autocomplete 為例

此篇就以 autocomplete 為例,什麼是 autocomplete 呢?
https://ithelp.ithome.com.tw/upload/images/20190930/201064262aOIsW1ZAu.jpg
就是會有一個搜尋的 input
https://ithelp.ithome.com.tw/upload/images/20190930/20106426GPkogMamX9.jpg
當你輸入文字時會自動出現相對應的結果。打 "H" 就會出現 "H" 開頭的結果提示你
https://ithelp.ithome.com.tw/upload/images/20190930/20106426blG6u3Z7U1.jpg
打 "Ha" 同理會出現 "Ha" 開頭的結果

我以前都會這樣做

其實這功能很常實作到,也不困難。我都直接用 Array.prototype.filter 資料然後秀出來就完成!

但原來這只適用資料量不大的情況。因為使用者輸入一次你就要 filter 一次,假如使用者頻繁輸入就可能會影響到效能

解決辦法

Debounce

設一個 timeout,例如輸入以後過兩秒才去抓一次使用者輸入值

function debounce(func, delay){
  // timeout 初始值
  let timeout = null;
  return function(){
    let context = this;  // 指向 myDebounce 這個 input
    let args = arguments;  // KeyboardEvent
    clearTimeout(timeout)

    timeout = setTimeout(function(){
      func.apply(context, args)
    }, delay)
  }

}
debounce(function(e){
        console.log(e.target.value);
}, 2000)

https://ithelp.ithome.com.tw/upload/images/20191002/20106426sy2aaxNKjA.jpg
當你輸入 "H" 過 2 秒就會 print "H"(實務上就是過 2 秒才 autocomplete 去抓相關資料)。這應該沒甚麼問題
https://ithelp.ithome.com.tw/upload/images/20191002/20106426kJIzDPCQzK.jpg
但當使用者一直頻繁輸入,

  • 第 0 秒時輸入 "H",程式會把 "H" 放入 event queue 裡面不執行,等待兩秒後再執行
  • 使用者又在第 1 秒時輸入 "a",這時清掉 event queue 的東西(clearTimeout),重新放 "Ha" 進 event queue,從新等待 2 秒後再執行
  • 在第 2 秒時又輸入 "sssss",這時再次清掉 event queue 的東西,重新放 "Hasssss" 進 event queue,重新等待 2 秒後再執行
  • 第 4 秒時,印出 "Hasssss"

好處就是可以防止 js 頻繁去篩選資料,等待兩秒後再一次篩選就好

Throttle

它防止一下輸入太頻繁的值,所以會限制一個時間,在這時間內觸發的值會被存到下一次再存

function throttle(func, delay){
  let inThrottle;
  let timeout = null;
  return function(){
    let context = this;
    let args = arguments;
    if(!inThrottle){
      // 輸入之後兩秒內都不回進入這邊
      func.apply(context, args)
      inThrottle = true;
      clearTimeout(timeout);
      timeout = setTimeout(function(){
        inThrottle = false
      }, delay)
    }
  }
}
throttle(function(e){
        console.log(e.target.value);
}, 2000)

https://ithelp.ithome.com.tw/upload/images/20191002/20106426qGad5ih6IS.jpg
當你第 0 秒輸入 "H" 馬上會印 "H",接下來兩秒內程式都不會再印東西
https://ithelp.ithome.com.tw/upload/images/20191002/20106426xs5ywdYN8R.jpg

直到第 2 秒才會印出來。所以以此類推在 第 0 秒、第 2 秒、第 4 秒時才會印出東西。

看到這邊應該可以感受到 Debounce 跟 Throttle 不同了。以輸入這個例子我是覺得會比較適合用 Debounce。但如果是偵測滑鼠移動 Throttle 就比較適合了。

這邊有我試做的 範例 可以上去玩玩看

參考資料

如有錯誤或需要改進的地方,拜託跟我說。
我會以最快速度修改,感謝您

上一篇
[LeetCode #167] Two Pointer
下一篇
夢想與現實的差距之國外工作亂聊最終章
系列文
前端工程師用 javaScript 學演算法32

尚未有邦友留言

立即登入留言