throttle 與 debounce
debounce (延遲執行)
應用: auto complete input
打字的間隔很近不用那麼快發 request。call api 的 function 不要每打一次就出發,而是要確保 x 秒後才觸發。
// 每當 input 的內容有變動,就呼叫 handleChange
$('input').change(handleChange)
// 讓原本發後端 api 的函式 debounce
const debouncedFn = debounce(getAutoSuggestions, 250)
function handleChange(e) {
// 拿到 input 的值
const value = e.target.value
// 發 api 去後端拿搜尋建議,然後 render 出來
// 細節我就不寫了
// 在 250ms 內重複呼叫的話不會有反應
debouncedFn(value)
}
這樣的做法不是正確的。
function debounce(fn, ms) {
setTimeout(() => {
fn() // 執行 function
}, ms)
}
打第一個字 => 250 毫秒才會執行 getAutoSuggestions
打第二個字 => 250 毫秒才會執行 getAutoSuggestions,但可能第一個已經發出去了
所以在 setTimeout 到期之前若觸發 handleChange 應該將前一個 request 給取消
$('input').change(handleChange)
function handleChange(e) {
const value = e.target.value
setTimeout(() => {
getAutoSuggestions(value)
}, 250)
}
加上 timer 目的是達到 setTimeout 到期之前若觸發 handleChange 應該將前一個 request 給取消
若 timer 有東西就表示有計時器在這邊等,因此若計時器還沒觸發前又再呼叫一次,就要再重新設定計時器 => clearTimeout(timer)
我設置一個計時器 250 毫秒會被觸發
打第一個字 => timer => 250 ms
打第二個字 => 發現已有 timer => 將打第一個字的 timer (x) => 重設一個新的 timer => 250 ms
以此類推
$('input').change(handleChange)
let timer
function handleChange(e) {
const value = e.target.value
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
getAutoSuggestions(value)
}, 250)
}
要讓所有人都使用,做成一個函式
return 的 function 就是 debouncedFn
const debouncedFn = debounce(getAutoSuggestions, 250)
function debounce(fn, ms) {
let timer
return function(...args) {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
fn(...args)
}, ms)
}
}
debouncedFn()
debouncedFn()
memoize
不想改到原本的 function 希望創造出新的 function 有原本的功能
會出事 => if (!cached[num])
=> 如果 complex function 某個 input 的回傳結果為 0 => 這個的檢查就會是 true => 就會再重新算一遍
參數有多個就會碰到 cached 的 key 怎麼存。
function memorize(fn) {
const cached = {}
return function(num) {
if (!cached[num]) {
cached[num] = fn(num)
}
return cached[num]
}
}
const memorizeFn = memorize(complex)
console.log(memorizeFn(10))
function complex(n) {
return n * n
}
解決出事問題 => if (cached[num] === undefined)
如果 function 回傳 undefined 這就要去看原本 function 的實作是甚麼
若想做到完美,cached[num] 可以存一個物件
如果答案是 falsey 的話,就不會過判斷,如果算過因為是物件所以答案一定會是 truesey
function memorize(fn) {
const cached = {}
return function(num) {
if (!cached[num]) {
cached[num] = {
value: fn(num)
}
}
return cached[num].value
}
}
閉包的學習技巧
一行一行看、一行一行執行