underscore debounce函數分析

underscore debounce函數分析

標籤(空格分隔): underscoresegmentfault


本文是underscore源碼剖析系列第六篇文章,上節咱們介紹了throttle節流函數的實現,這節將會介紹一下節流函數的兄弟 —— debounce防抖動函數。
throttle函數是在高頻率觸發的狀況下,爲了防止函數的頻繁調用,將其限制在一段時間內只會調用一次。而debounce函數則是在頻繁觸發的狀況下,只在觸發的最後一次調用一次,想像一下若是咱們用手按住一個彈簧,那麼只有等到咱們把手鬆開,彈簧纔會彈起來,下面我用一個電梯的例子來介紹debounce函數。app

電梯

假如我下班的時候去坐電梯,等了一段時間後,電梯正準備關上門降低,這個時候一個同事走了過來,電梯門被打開,這樣電梯就會繼續等一段時間,若是中間一直有人進來,那麼電梯就一直不會降低,直到最後一我的進來後過了必定時間後尚未下一我的進來,這時電梯纔會降低。函數

應用場景

除了電梯,事實上咱們還有不少應用場景,好比我用鍵盤不斷輸入文字,我但願等最後一次輸入結束後纔會調用接口來請求展現聯想詞,若是每次輸入一個字的時候就會調用接口,這樣調用未免太過於頻繁了。this

沒有debounce時:
圖片描述spa

有debounce時:
圖片描述3d

簡單的debounce

知道debounce的工做原理了,咱們能夠先本身實現一個比較簡單的debounce函數。code

function debounce(func, wait) {
    var timeout,
        args, 
        context
    var later = function() {
        func.apply(context, args)
        timeout = context = args = null
    }
    return function() {
        context = this
        args = arguments
        // 每次觸發都清理掉前一次的定時器
        clearTimeout(timeout)
        // 只有最後一次觸發後纔會調用later
        timeout = setTimeout(later, wait)
    }
}

麻雀雖小,五臟俱全,不過這個函數仍是有不少問題,好比每次觸發都設置了太多的setTimeout,這樣會比較耗費cpu,咱們來看一下underscore的實現方式。blog

underscore debounce

// debounce函數傳入三個參數,分別是要執行的函數func,延遲時間wait,是否當即執行immediate
// 若是immediate爲true,那麼就會在wait時間段一開始就執行一次func,以後無論觸發多少次都不會再執行func
// 在相似不當心點了提交按鈕兩下而提交了兩次的狀況下頗有用
_.debounce = function (func, wait, immediate) {
        var timeout, args, context, timestamp, result;

        var later = function () {
            // 這個是最關鍵的一步,由於每次觸發的時候都要記錄當前timestamp
            // 可是later是第一次觸發後wait時間後執行的,_now()減去第一次觸發時的時間固然是等於wait的
            // 可是若是後續繼續觸發,那麼_.now() - timestamp確定會小於wait
            // last是執行later的時間和上一次觸發的時間差
            var last = _.now() - timestamp;
            // 若是在later執行前還有其餘觸發,那麼就會從新設置定時器
            // last >= 0應該是防止客戶端系統時間被調整
            if (last < wait && last >= 0) {
                timeout = setTimeout(later, wait - last);
            // 若是last大於等於wait,也就是說設置timeout定時器後沒有再觸發過
            } else {
                timeout = null;
                // 這個時候若是immediate不爲true,就會當即執行func函數,這也是爲何immediate爲true的時候只會執行第一次觸發
                if (!immediate) {
                    result = func.apply(context, args);
                    // 解除引用
                    if (!timeout) context = args = null;
                }
            }
        };

        return function () {
            context = this;
            args = arguments;
            // 每次觸發都用timestamp記錄時間戳
            timestamp = _.now();
            // 第一次進來的時候,若是immediate爲true,那麼會當即執行func
            var callNow = immediate && !timeout;
            // 第一次進來的時候會設置一個定時器
            if (!timeout) timeout = setTimeout(later, wait);
            if (callNow) {
                result = func.apply(context, args);
                context = args = null;
            }

            return result;
        };
    };

爲了防止出現咱們上面那種不停地設置定時器的狀況,underscore只在later函數中在上一次定時器執行結束後才從新設置定時器。
若是傳入的immediate爲true,那麼只會在第一次進來的時候當即執行。很明顯在上面代碼中func執行只有兩處,一個是callNow判斷裏面,一個是!immediate判斷裏面,因此這樣保證了後續觸發不會再執行func。
參考連接:
一、淺談throttle以及debounce的原理和實現接口

相關文章
相關標籤/搜索