# Debounce : 使用者輸入時 , 延緩表格的檢查
###### tags : `w3HexSchool` . `js`
> 有時我們會需要在用戶輸入帳戶名稱的同時 , 確認此帳戶名稱已存在
> 來提升註冊流程的舒適感 , 可是我們不可能在用戶輸入時 ,
> 一直呼叫 "用戶名稱是否存在 API" , 這樣 API & 用戶 4G 的流量會過高
> 因此我們需要 "使用者輸入時 , 延緩表格的檢查" 這樣的功能
> 不過 , 這該如何實現呢 ? 🤔
> 讓我們請出今天的主角 `debounce` 函式 來幫忙吧 😘 !
- [debounce 與一般滑鼠移動的比較](http://demo.nimius.net/debounce_throttle/)
![](https://i.imgur.com/Zyx9xI7.png)
> 確認上方功能是我們想要的 , 之後就來 `使用` 吧!
下載 `lodash`
```
npm i -s lodash
```
引用 `lodash`
```
import _ from 'lodash'
```
> 剛開始使用 `debounce` 時 , 我以為直接執行就成了 , 結果...
- [debounce 文件](https://lodash.com/docs/4.17.15#debounce)
![](https://i.imgur.com/AUv7NJS.png)
看 `debounce` 文件時 , 我天真的以為代入 2 個參數就可執行了 , 結果發現沒有任何反應
```javascript=
_.debounce(()=>console.log('debounce trigger'),1000)
```
console.log 後 , 才知道 `_.debounce` 會回傳 function
```javascript=
const x = _.debounce(()=>console.log('debounce trigger'),1000);
console.log('x=',x);
```
![](https://i.imgur.com/bsRhFm9.png)
所以我多加 () 去執行回傳的函數 , 這次看起來好像沒問題 ?
```javascript=
(_.debounce(()=>console.log('debounce trigger'),1000))();
```
![](https://i.imgur.com/1phkzzr.png)
當我多次執行時 , 發現他沒有延遲執行 , 發生了甚麼事情 😱 ?
![](https://i.imgur.com/56OouKn.png)
> 近期找到[一篇文章](https://mropengate.blogspot.com/2017/12/dom-debounce-throttle.html) , 它分享了 `debounce` 函數的長相 , 我才瞭解說為何不能當作函數直接呼叫 , 原來每次呼叫 `debounce` 都會建一個全新的 timer
```javascript=
function debounce(func, delay) {
var timer = null;
return function () {
var context = this;
var args = arguments;
clearTimeout(timer);
timer = setTimeout(function () {
func.apply(context, args)
}, delay);
}
}
```
正確用法 , 應該長這樣 , 我們只能執行回傳的函式
```javascript=
const x = _.debounce(()=>console.log('debounce trigger'),1000);
x();
x();
x();
```
![](https://i.imgur.com/5sQ6E2V.png)
> 每次使用時 , 都需要在 create 階段建一個函數放到 this 中 , 感覺使用起來很不方便啊 !
```javascript=
export default {
name: "Comp",
create() {
this.nameDebounceFn = _.debounce(()=>console.log('validate name'),1000);
this.accountDebounceFn = _.debounce(()=>console.log('validate account'),1000);
},
data() {
return {
this.nameDebounceFn: null,
this.accountDebounceFn: null,
}
}
}
```
> 讓我們做一個 getDebounceFunc 函式 , 來輔助我們使用 `debounce` 吧 !
建立 `getDebounceFunc`
```javascript=
// in debounceUtils.js
const debounceMap = {};
const getDebounceFunc = (debounceId, wait = 1000) => {
const tempFunc = debounceMap[`${debounceId}`];
if (tempFunc) return tempFunc;
else {
const newTempFunc = debounce(func => func(), wait);
debounceMap[`${debounceId}`] = newTempFunc;
return newTempFunc;
}
};
```
使用 `getDebounceFunc`
```javascript=
// 使用說明
export default {
name: "Comp",
watch: {
account(value) {
getDebounceFunc('validateAccount')(()=>{
console.log('validate account');
});
},
name(value) {
getDebounceFunc('validateName')(()=>{
console.log('validate name');
});
},
},
data() {
return {
account:'',
name:''
}
}
}
```
> 包裝成 util 後 , 有幾點好處
> 1. 不用在建立 debounce 時 , 就必須決定要執行的內容
> 2. 不用將 debounce 回傳的函式暫存到 this 中
## 結語
當初會需要使用 `debounce` 是因為 使用者輸入品號時 , 一直 request API 確認 品號是否重複 , 影響到 API 的效能 , 因此導入 `debounce` 函式使用
只是 , 當初導入時 , 沒想到 `debounce` 的使用這麼複雜 , 因此在此紀錄 , 以便下次使用 `debounce` 又踩坑
## 參考資料
- [網頁 DOM 事件的效能優化:Debounce 和 Throttle](https://mropengate.blogspot.com/2017/12/dom-debounce-throttle.html)
- [How to import lodash to Chrome console?](https://medium.com/@matt.leo/how-to-import-lodash-to-chrome-console-3e5e30b4933e)