白話debounceとthrottle

4019 ワード

問題
開発中に頻度の高いイベントや連続的なイベントに遭遇し、パフォーマンスの最適化を行わないと、ページカートンなどの現象が発生する可能性があります.
  • マウスイベント:mousemove(ドラッグ)/mouseover(スクライブ)/mouseWheel(ロールスクリーン)
  • キーボードイベント:keypress(ajaxベースのユーザ名一意性チェック)/keyup(テキスト入力チェック、自動完了)/keydown(ゲーム中の射撃)
  • windowのresize/scrollイベント(DOM要素動的位置決め)
  • このような問題を解決するためによく使われる方法はthrottle(スロットル)とdebounce(ジッタ除去)である.throttle(スロットル)とdebounce(ジッタ除去)は、ある関数が一定時間に何回実行されるかを制御するためのソリューションであり、両者は似ていて異なる.
    次に、両者の類似と違いを具体的に見てみましょう.
    throttleとdebounceを知っています
    throttleとdebounceの役割は,イベントの実行方法とタイミングを確認することであり,従来は両者の違いがよく分からず,両者を混同しやすい.
    次は2つの簡単なシーンでdebounceとthrottleについて説明します.後でこの2つのシーンが混同されないと思います.
    debounce
              ,                 ,    ,        ,       ; 
              ,     ,      ;          ,        ,       ,
    

    所以debounce的作用是,当调用动作触发一段时间后,才会执行该动作,若在这段时间间隔内又调用此动作则将重新计算时间间隔

    throttle
              ,                 ,    ,        ,       ;  
      ,        ,
          ,         ,         ,     ,
    

    所以throttle的作用是,预先设定一个执行周期,当调用动作的时刻大于等于执行周期则执行该动作,然后进入下一个新的时间周期

    简单实现

    有了上面的了解,就可以去实现简单debounce和throttle了。

    debounce实现

    首先来看看debounce的实现,根据前面对debounce的描述:

    1. debounce函数会通过闭包维护一个timer
    2. 当同一action在delay的时间间隔内再次触发,则清理timer,然后重新设置timer

    可以在Chrome中运行下面的代码,看看debounce的效果,代码Github链接:

    var debounce = function(action, delay) {
        var timer = null;
        
        return function() {
            var self = this, 
                  args = arguments;
                  
            clearTimeout(timer);
            timer = setTimeout(function() {
                action.apply(self, args)
            }, delay);
        }
    }
    
    // example
    function resizeHandler() {
        console.log("resize");
    }
    
    window.onresize = debounce(resizeHandler, 300);
      
      

    throttle実装
    throttleとdebounceの最大の違いは、throttleにはバルブ値があり、バルブ値に達するとactionは必ず1回実行されます.
    したがってthrottleの実装は、前のdebounceの実装に基づいて、バルブ値を追加するだけで、コードGithubリンクを追加することができます.
    var throttleV1 = function(action, delay, mustRunDelay) {
        var timer = null,
              startTime;
              
        return function() {
            var self = this, 
                  args = arguments, 
                  currTime = new Date();
                  
            clearTimeout(timer);
            
            if(!startTime) {
                startTime = currTime;
            }
            
            if(currTime - startTime >= mustRunDelay) {
                action.apply(self, args);
                startTime = currTime;
            }
            else {
                timer = setTimeout(function() {
                    action.apply(self, args);
                }, delay);
            }
        };
    };

    実際には、上記の実装は、パッケージを閉じて最初の時間を維持するだけで、心を簡素化することができます.
    var throttleV2 = function(action, delay){
        var statTime = 0;
        
        return function() {
            var currTime = +new Date();
            
            if (currTime - statTime > delay) {
                action.apply(this, arguments);
                statTime = currTime ;
            }
        }
    }    
    
    // example
    function resizeHandler() {
        console.log("resize");
    }
    
    window.onresize = throttleV2(resizeHandler, 300);

    まとめ
    前の紹介を通じて、debounceとthrottleに対して直観的な認識があるはずです.
  • debounce:非常に頻繁にトリガーされるイベントを1回の実行
  • に統合する
  • throttle:バルブ値を設定し、バルブ値内でトリガされたイベントを1回に統合して実行します.バルブ値に達すると、必ずイベント
  • が実行する.
    throttleとdebounceについて理解した後、一般的なシーンを見てみましょう.
    debounce
  • キーボードイベントについては、ユーザの入力が比較的頻繁である場合、debounceによりキーボードイベント処理
  • をマージすることができる.
  • ajaxリクエストの場合、例えば、ページドロップダウンが一定の戻り値を超えるとajaxによって新しいページコンテンツが要求され、debounceによってajaxリクエストイベント
  • がマージする.
    throttle
  • キーボードイベントでは、ユーザーが頻繁に入力しますが、一定時間(バルブ値)で処理関数を実行する必要がある場合はthrottleを使用します.
  • 例えば、いくつかのウェブゲームのキーボードイベント
  • マウスの移動とウィンドウのスクロールに対して、マウスの移動とウィンドウのスクロールは大量のイベントをもたらしますが、しばらくの間、ページの効果を見なければなりません.
  • 例えばドラッグ可能なdivの場合、debounceを使用すると、divはドラッグが停止した後に一気にターゲット位置にジャンプする.この場合、throttle
  • を使用する必要があります.