debounceとthrottleの違い

10234 ワード

2011年にTwitterサイトで問題が発生しました.ホームページを下にスクロールすると、ページが遅くなり、応答がないということです.John Resiggは「a blog post about the proble」という記事を発表しました.scroll事件に直接結びつけられて消耗が高いことを指摘しました.現在のプロジェクトでは、似たようなscrollまたはresizeイベントを節流制御しています.以下のように、私たちはよく使います.また、「JavaScript高級プログラム設計」-JavaScript上級技術で言及された節流方式です.
/**
 *     (JavaScript      )
 * @param method   
 * @param scope          
 */
function throttle(method, scope) {
    clearTimeout(method.tId);
    method.tId= setTimeout(function(){
        method.call(scope);
    }, 100);
}

function resizeDiv(){
    var div = document.getElementById("myDiv");
    div.style.height = div.offsetWidth + "px";
}

//    resize      
window.onresize = function(){
    throttle(resizeDiv);
};
前はずっと上記のコードは本当の節流を実現したと思っていましたが、まだ深く研究していません.最近までは前の同僚とグラフの問題を討論していましたが、「throttleとdebounce」と言いました.彼は私達のプロジェクトでは本当の意味でのthrottleではなく、debounceの簡単な実現だと言いました.ここではまず、「throttleとdebounce」を簡単に紹介します.両方とも時間の経過に従って実行関数の回数を制御して、より少ない資源消耗を達成します.特に、イベントのトリガにおいて、特に重要です.
  • debounce(func, wait, immediate):関数のアンチホップバージョンを作成し、リターンし、遅延関数の実行(真の実行)を関数の最後の呼び出し時刻のwait ms後に、いくつかの入力(複数のユーザ操作)が停止した後に実行しなければならない動作に役立つ.連続した呼び出しを一つにまとめる!
  • throttle(func, wait, options):節流弁のような関数を作成して返します.関数を繰り返し起動すると、指定されたwait msごとに最大一回の関数が呼び出されます.許可しない方法はwait msごとに一回以上実行します.
  • 例えば、ページにはボタンがあります.throttleとdebounceを通じてその傍受関数が含まれています.waitは1000 msに設定されています.各1000 ms以内に何度もclickを触発して20000 ms継続することを確保する.
    //   1 (      1000ms )
    btnDom.addEventListener('click', debounce(clickBtn, 1000)); 
    //   3 (        、1000ms     ,2000ms     )
    btnDom.addEventListener('click', throttle(clickBtn, 1000)); 
    debounce使用シーン:
    最初のトリガの後、カウントダウンを行います.カウントダウン中に他のトリガがあればカウントダウンをリセットします.そうでないとfnを実行します.これを使って、流量が遅くなるまで、密集作業を繰り返して廃棄します.たとえば:
  • によるユーザ入力の検証は、入力中に処理されず、入力を停止して検証するだけで十分である.
  • によりajaxが提出された場合、1 sにおける大量の要求が繰り返し送信されることを望まない.
  • throttle使用シーン
    最初のトリガの後にfnを実行し(もちろん{leading: false}によってキャンセルすることができる)、その後、wait msの後に再度実行し、単位wait ms内のすべての繰り返しトリガを破棄する.つまり連続的な継続的トリガがある場合、wait msごとにfnを実行する.debounceと同じ用例ですが、一定間隔で実行しなければならないコールバック関数を保証したいです.たとえば:
  • は、入力を停止してから検証するのではなく、n秒ごとに検証する.
  • は、マウスのスクロール、window.resizeに対して節流制御を行う.
  • 正真のビジネスシーン:
    かなり一般的な例では、ユーザーが無限にスクロールしているページの上から下にスクロールしてページをロードします.ページの下からどれぐらい離れているかを判断する必要があります.ユーザーが下に近づいたら、私たちは要求を送ります.もっと多くのコンテンツをページにロードします.このdebounceは、ユーザがローリングを停止したときだけトリガされるので、無駄です.しかし、ユーザが底に近づいたときに要求する必要があります.throttleを通して、底面からどれぐらいの距離があるかを連続的に監視することができます.
    $(document).ready(function(){
      //          300ms
      $(document).on('scroll', throttle(function(){
        check_if_needs_more_content();
      }, 300));
    
      //           
      function check_if_needs_more_content() {     
        var pixelsFromWindowBottomToBottom = 0 + $(document).height() - $(window).scrollTop() - $(window).height();
        //            200,      
        if (pixelsFromWindowBottomToBottom < 200){
          //       
          $('body').append($('.item').clone()); 
        }
      }
    });
    特別な説明:
    //   
    $(window).on('scroll', function() {
        debounce(doSomething, 300); 
    });
    
    //   
    $(window).on('scroll', debounce(doSomething, 200));
    下記コードは、undersscoreより引用されます.
    debounce関数
    /**
     *    。func            wait      !
     * @param func     
     * @param wait     
     * @param immediate  true,debounce  wai              
     * @returns {Function}
     */
    function debounce(func, wait, immediate) {
        var timeout, args, context, timestamp, result;
    
        var later = function() {
            var last = new Date().getTime() - timestamp; // timestamp     
    
            if (last < wait && last >= 0) {
                timeout = setTimeout(later, wait - last);
            } else {
                timeout = null;
                if (!immediate) {
                    result = func.apply(context, args);
                    if (!timeout) context = args = null;
                }
            }
        };
    
        return function() {
            context = this;
            args = arguments;
            timestamp = new Date().getTime();
            var callNow = immediate && !timeout;
    
            if (!timeout) {
                timeout = setTimeout(later, wait);
            }
            if (callNow) {
                result = func.apply(context, args);
                context = args = null;
            }
            return result;
        };
    }
    スロットル関数
    /**
     *                 ,          ,     wait         
     * @param func     
     * @param wait     
     * @param options                ,  {leading: false},
     *                              ,  {trailing: false}
     * @returns {Function}
     */
    function throttle(func, wait, options) {
        var context, args, result;
        var timeout = null;
        var previous = 0;
        if (!options) options = {};
        var later = function() {
            previous = options.leading === false ? 0 : new Date().getTime();
            timeout = null;
            result = func.apply(context, args);
            if (!timeout) context = args = null;
        };
        return function() {
            var now = new Date().getTime();
            if (!previous && options.leading === false) previous = now;
            var remaining = wait - (now - previous);
            context = this;
            args = arguments;
            if (remaining <= 0 || remaining > wait) {
                if (timeout) {
                    clearTimeout(timeout);
                    timeout = null;
                }
                previous = now;
                result = func.apply(context, args);
                if (!timeout) context = args = null;
            } else if (!timeout && options.trailing !== false) {
                timeout = setTimeout(later, remaining);
            }
            return result;
        };
    }