去抖函式的實現

PsChina發表於2019-03-06

最近偶爾也會出去面面試,兩次被問到函式去抖和函式截流,特別是公司專案沒有引入loadsh庫,剛好也需要用到去抖,博主在一家交易所(虛擬幣交易)上班,有一次,進行幣幣交易的時候,下單時由於網路延遲比較高,下單頁面沒有及時跳轉,以為沒有成功,就多點選了一下,結果下了兩單...我靠這是bug呀(第一版是外包做的)嚇得我趕緊寫了個去抖函式。

Vue.prototype.is_locked = Symbol('is_loacked') // 定義一個不存在的變數 當做函式是否被鎖的屬性
Vue.prototype.debounce = function(func = _ => undefiened, interval = 0, ...args) {
    const { is_locked } = Vue.prototype
    if(func[is_locked]){
        return
    }
    func[is_locked] = true
    func.apply(this, args)
    setTimeout(_ => func[is_locked] = false, interval)
}
複製程式碼

這個函式的思路是實現一個代理函式debounce 將你的函式func作為引數傳遞給debounce,以及函式觸發後應該被鎖定的時間interval,並且告訴debounce要執行的函式的引數args。 代理函式在執行 func 之前會檢查這個函式是否處於鎖定期(is_locked),若果處於鎖定期直接結束函式,否則就會先給函式上鎖,然後立即呼叫,之後會在約定解鎖的時間將鎖解除。

而函式截流,可以通過封裝去抖函式實現,也可以單獨實現,暫時就不討論了?。

以上這個版本還有一些缺陷,那就是當某個函式被大量複用的時候,會造成其他呼叫者也無法呼叫的情況,我們需要改進一下這個函式,讓debounce能聰明的識別不同的呼叫者讓其對不同的呼叫者獨立記時。

改版

Vue.prototype.$is_locked = Symbol('$is_locked')
Vue.prototype.$caller_set = Symbol('$caller_set')
// 函式去抖
Vue.prototype.debounce = function (func = _ => undefined, interval = 0, ...args) {
    const {$is_locked, $caller_set} = Vue.prototype
    // 如果函式因為未達到解鎖時間而處於鎖定狀態,直接結束函式
    if (func[$is_locked] && func[$caller_set] && func[$caller_set].has(this)) return
    // 否則鎖住這個函式
    func[$is_locked] = true
    if(func[$caller_set] === undefined){
        func[$caller_set] = new Set()
    }
    // 把呼叫者加入set
    func[$caller_set].add(this)
    // 立即呼叫
    func.apply(this, args)
    // 等達到去抖保護時間後解鎖函式,並且去掉呼叫者。
    setTimeout(_ => {
        func[$is_locked] = false
        func[$caller_set].delete(this)
    }, interval)
}
複製程式碼

相關文章