JavaScript特定のテーマのはundersscore学の節流に従います.


JavaScript特定のテーマシリーズの第二編は、節流を解説し、ゼロからundersscoreのthrottle関数を実現します.
前言
『JavaScript特定のテーマのundersscoreに従って、震え防止を学ぶ』において、なぜ事件の頻繁なトリガを制限するのか、及びどのように制限するのかを理解しました.
  • debounce手ぶれ防止
  • スロットル・スロットル
  • 今日は節流の実現を重点的に講義する.
    流れをよくする
    流れの原理は簡単です.
    イベントを継続的に起動すると、一定期間ごとにイベントが実行されます.
    初めて実行するかどうかや終了後に実行するかどうかによって、効果は異なります.私たちはleadingで初めて実行するかどうか、triling代表が終わったらもう一度実行しますか?
    節流の実現については、2つの主流の実装があり、1つはタイムスタンプを使用し、1つはタイマーを設定することである.
    タイムスタンプを使う
    最初の方法を見てみましょう.タイムスタンプを使って、イベントをトリガする時、現在のタイムスタンプを取り出して、それから前のタイムスタンプを引いて(最初の開始値を0にします.)、設定した時間周期より大きい場合は関数を実行して、タイムスタンプを以前のタイムスタンプに更新します.もし小さいなら、実行しません.
    この表現を見たら、コードはもう書けると思いますか?第一版のコードを書きましょう.
    //    
    function throttle(func, wait) {
        var context, args;
        var previous = 0;
    
        return function() {
            var now = +new Date();
            context = this;
            args = arguments;
            if (now - previous > wait) {
                func.apply(context, args);
                previous = now;
            }
        }
    }
    例は依然としてdebounceの例を使っています.
    container.onmousemove = throttle(getUserAction, 1000);
    効果のデモンストレーションは以下の通りです.
    マウスを移動すると、イベントはすぐに実行され、1秒ごとに実行されます.4.2 sでトリガを停止したら、後でイベントは実行されません.
    タイマーを使う
    次に、第二の実現方式について話します.タイマーを使います.
    イベントをトリガする時、私達はタイマーを設定して、イベントをトリガする時、タイマーが存在するなら、タイマーが実行されるまでは実行しません.そして関数を実行して、タイマーをクリアして、次のタイマーを設定することができます.
    //    
    function throttle(func, wait) {
        var timeout;
        var previous = 0;
    
        return function() {
            context = this;
            args = arguments;
            if (!timeout) {
                timeout = setTimeout(function(){
                    timeout = null;
                    func.apply(context, args)
                }, wait)
            }
    
        }
    }
    効果をより明確にするために、waitを設定する時間は3 sです.効果は以下の通りです.
    マウスを移動した時には、イベントはすぐに実行されません.3 s揺れた後にやっと実行されました.その後、3 sごとに実行されます.数字が3と表示された時、直ちにマウスを移動して、約9.2 sの時にトリガを停止しますが、まだ12 s目の時に実行されます.
    二つの方法を比較します.
  • 第一のイベントは直ちに実行され、第二のイベントはn秒後に初めて
  • が実行される.
  • 第一のイベント停止トリガが発生した後、イベントを再実行することができませんでした.第二のイベント停止トリガが発生した後も、再度イベント
  • を実行します.
    両刀両璧
    どんなものがほしいですか?
    ある人が言いました.「尾行がほしいです.マウスを移動するとすぐに実行できます.トリガーを停止するともう一度実行できます.
    ですから、私たちは両者の優位性を総合して、双剣と折衷して、コードを書きます.
    //    
    function throttle(func, wait) {
        var timeout, context, args, result;
        var previous = 0;
    
        var later = function() {
            previous = +new Date();
            timeout = null;
            func.apply(context, args)
        };
    
        var throttled = function() {
            var now = +new Date();
            //     func      
            var remaining = wait - (now - previous);
            context = this;
            args = arguments;
             //                    
            if (remaining <= 0 || remaining > wait) {
                if (timeout) {
                    clearTimeout(timeout);
                    timeout = null;
                }
                previous = now;
                func.apply(context, args);
            } else if (!timeout) {
                timeout = setTimeout(later, remaining);
            }
        };
        return throttled;
    }
    効果のデモンストレーションは以下の通りです.
    マウスを移動すると、イベントはすぐに実行され、3 s揺れ、イベントは再び実行され、数字が3になると、すなわち6 s後にマウスを移動し、トリガーイベントを停止し、9 sの時には、再びイベントを実行します.
    最適化
    でも、私は時々頭がないとか、尻尾がないとか、これはどうすればいいですか?
    私たちは3番目のパラメータとしてoptionsを設定して、どのような効果があるかを伝える値によって判断します.
    leading:falseは、最初のtrilingの実行を禁止することを表します.falseは停止トリガを無効にするコールバックを表します.
    コードを変えましょう.
    //    
    function throttle(func, wait, options) {
        var timeout, context, args, result;
        var previous = 0;
        if (!options) options = {};
    
        var later = function() {
            previous = options.leading === false ? 0 : new Date().getTime();
            timeout = null;
            func.apply(context, args);
            if (!timeout) context = args = null;
        };
    
        var throttled = 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;
                func.apply(context, args);
                if (!timeout) context = args = null;
            } else if (!timeout && options.trailing !== false) {
                timeout = setTimeout(later, remaining);
            }
        };
        return throttled;
    }
    キャンセル
    debounceの実現に、私達はcancelの方法を加えました.throttleは私達もcancelの方法を加えました.
    //          ,                
    ...
    throttled.cancel = function() {
        clearTimeout(timeout);
        previous = 0;
        timeout = null;
    }
    ...
    注意
    アンダースコアの実現にはこのような問題があります.leading:falsetrailing: falseは同時に設置できないということです.
    同時に設定すると、例えばマウスを移動すると、trilingがfalseに設定されていますので、トリガーを停止するとタイマーが設定されません.設定した時間を過ぎてから移動すると、すぐに実行されます.leading:falseに違反して、bugが出てきます.このthrottleは3つの使い方しかありません.
    container.onmousemove = throttle(getUserAction, 1000);
    container.onmousemove = throttle(getUserAction, 1000, {
        leading: false
    });
    container.onmousemove = throttle(getUserAction, 1000, {
        trailing: false
    });
    これまで完全にundersscoreの中のthrottle関数を実現しました.おめでとうございます.
    デモコード
    関連コードはGithubブログ倉庫にあります.
    シリーズ
    JavaScript特定のテーマシリーズのディレクトリアドレス:https://github.com/mqyqingfeng/Blog.
    JavaScriptテーマシリーズは二十編ぐらい書く予定で、主に日常開発の中の機能点の実現を研究しています.例えば、手ぶれ、節流、過重、類型判断、コピー、最値、扁平、柯里、再帰、乱序、ソートなど、研究(xi)undescoreとjQueryの実現方式が特徴です.
    間違いや不備があったら、ぜひ指摘してください.ありがとうございます.好きだったり、何かを啓発したりすれば、starを歓迎し、作者に対しても励みになります.