ホームページ  >  記事  >  ウェブフロントエンド  >  JavaScriptのスロットル機能Throttleの詳細説明

JavaScriptのスロットル機能Throttleの詳細説明

黄舟
黄舟オリジナル
2017-03-07 14:35:471109ブラウズ

ブラウザのDOMイベントには、ユーザーの操作に合わせて継続的にトリガーされるイベントがいくつかあります。たとえば、ブラウザ ウィンドウのサイズを変更し (resize)、ブラウザ ページをスクロールし (scroll)、マウスを移動します (mousemove)。つまり、ユーザーがこれらのブラウザ操作をトリガーすると、対応するイベント処理メソッドがスクリプトにバインドされている場合、このメソッドは継続的にトリガーされます。

これは私たちが望んでいることではありません。イベント処理メソッドが比較的大きく、DOM 操作が複雑で、そのようなイベントが継続的にトリガーされる場合、パフォーマンスの損失が発生し、ユーザー エクスペリエンスの低下につながることがあるためです (UI の応答は遅い、ブラウザがフリーズするなど)。したがって、一般的に言えば、対応するイベントに遅延実行ロジックを追加します。

通常、この関数を実装するには次のコードを使用します:

var COUNT = 0;
function testFn() { console.log(COUNT++); }
// 浏览器resize的时候
// 1. 清除之前的计时器
// 2. 添加一个计时器让真正的函数testFn延后100毫秒触发
window.onresize = function () {
    var timer = null;
    clearTimeout(timer);

    timer = setTimeout(function() {
        testFn();
    }, 100);
};

注意してください。上記のコードは実際には間違っていることがわかります。これは初心者が犯す問題です。setTimeout 関数の戻り値は相対値に保存する必要があります。そうしないと、サイズが変更されるたびに新しいタイマーが生成され、送信した効果が得られないため、コードを修正しました:

var timer = null;
window.onresize = function () {
    clearTimeout(timer);
    timer = setTimeout(function() {
        testFn();
    }, 100);
};

現時点では、コードは正常ですが、もう 1 つ新しい問題があります。 - グローバル変数タイマーが生成されます。これは望ましくないことです。このページにタイマーと呼ばれる他の機能がある場合、異なるコードが競合することになります。この問題を解決するには、JavaScript の言語機能、

closure

クロージャを使用する必要があります。関連する知識がある読者は、MDN にアクセスして詳細を学ぶことができます。 変更されたコードは次のとおりです:

/**
 * 函数节流方法
 * @param Function fn 延时调用函数
 * @param Number delay 延迟多长时间
 * @return Function 延迟执行的方法
 */
var throttle = function (fn, delay) {
    var timer = null;

    return function () {
        clearTimeout(timer);
        timer = setTimeout(function() {
            fn();
        }, delay);
    }
};
window.onresize = throttle(testFn, 200, 1000);
タイマーを内部に配置し、遅延処理関数を返すためにクロージャー関数 (スロットル調整) を使用します。そのため、タイマー変数は次のようになります。外部からは見えませんが、内部遅延関数がトリガーされたときにタイマー変数にアクセスすることもできます。

もちろん、この書き方は初心者にとっては理解しにくいです。理解できるように書き方を変更することができます:

var throttle = function (fn, delay) {
    var timer = null;

    return function () {
        clearTimeout(timer);
        timer = setTimeout(function() {
            fn();
        }, delay);
    }
};

var f = throttle(testFn, 200);
window.onresize = function () {
    f();
};

理解すべき主なポイントは次のとおりです:

Throttle

呼び出された後に返される関数は実際のものです。 onresize 関数 がトリガーされたときに呼び出す必要があります。このメソッドは完璧に近いように見えますが、実際の使用ではそうではありません。例:

ユーザーがブラウザウィンドウのサイズを
継続的に

変更すると、遅延処理関数は一度も実行されなくなります

そのため、別の関数を追加する必要があります: ユーザーがサイズ変更をトリガーしたとき、それは
である必要があります。一定時間 一定期間内に少なくとも1回トリガーします

。この判定条件は、各関数の呼び出し時に、最後の呼び出し時刻から現在時刻を減算することになります。次に、その差が一定期間より大きい場合は、直接トリガーされます。それ以外の場合は、タイムアウトの遅延ロジックが引き続き使用されます。 次のコードで注意する必要があるのは:

    前の変数にはタイマーと同様の機能があり、最後の識別を記録し、相対グローバル変数である必要があります
  1. ロジックフローが「at」の場合最小トリガー「1 回」ロジックの場合、関数呼び出しを現在の時刻にリセットする必要があります。簡単に言うと、次回と比較して、前回の時刻が実際には現在の時刻です
  2. /**
     * 函数节流方法
     * @param Function fn 延时调用函数
     * @param Number delay 延迟多长时间
     * @param Number atleast 至少多长时间触发一次
     * @return Function 延迟执行的方法
     */
    var throttle = function (fn, delay, atleast) {
        var timer = null;
        var previous = null;
    
        return function () {
            var now = +new Date();
    
            if ( !previous ) previous = now;
    
            if ( now - previous > atleast ) {
                fn();
                // 重置上一次开始时间为本次结束时间
                previous = now;
            } else {
                clearTimeout(timer);
                timer = setTimeout(function() {
                    fn();
                }, delay);
            }
        }
    };
  3. 実践:

時間を節約するためにウィンドウをスクロールするシナリオ、つまり、ユーザーがページを下にスクロールするとき、DOM 位置の計算や、DOM 要素の継続的な操作を必要とするその他のアクションなど、いくつかのメソッドの実行を抑制する必要があります

完全なコードは次のとおりです。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>throttle</title>
</head>
<body>
    <p style="height:5000px">
        <p id="demo" style="position:fixed;"></p>
    </p>
    <script>
    var COUNT = 0, demo = document.getElementById(&#39;demo&#39;);
    function testFn() {demo.innerHTML += &#39;testFN 被调用了 &#39; + ++COUNT + &#39;次<br>&#39;;}

    var throttle = function (fn, delay, atleast) {
        var timer = null;
        var previous = null;

        return function () {
            var now = +new Date();

            if ( !previous ) previous = now;
            if ( atleast && now - previous > atleast ) {
                fn();
                // 重置上一次开始时间为本次结束时间
                previous = now;
                clearTimeout(timer);
            } else {
                clearTimeout(timer);
                timer = setTimeout(function() {
                    fn();
                    previous = null;
                }, delay);
            }
        }
    };
    window.onscroll = throttle(testFn, 200);
    // window.onscroll = throttle(testFn, 500, 1000);
    </script>
</body>
</html>

テストには 2 つのケースを使用します。効果は、少なくともトリガーのパラメータを追加し、追加しないことです:

// case 1
window.onscroll = throttle(testFn, 200);
// case 2
window.onscroll = throttle(testFn, 200, 500);

ケース 1

パフォーマンスは次のとおりです: ページのスクロール中に testFN は呼び出されませんプロセス (停止できません) であり、停止時に 1 回呼び出されます。つまり、スロットルでの last setTimeout の実行であると言われており、その効果は図に示すとおりです (元の gif 画像を表示):

ケース 2

は次のように動作します: testFN はページスクロールプロセス中に初めて呼び出されます (停止できません) (少なくとも遅延ロジックから) 500 ミリ秒だけ実行を遅延させ、その後少なくとも 500 ミリ秒ごとに実行します. 効果は図の通りです

ここまでで、達成したい効果は概ね完成しました。読者は、これが指す関数、戻り値のストレージなど、後続の補助的な最適化を自分で理解できます。

引用

    テストコード http://jsbin.com/tanuxegija/edit
  1. 完全版コード http://jsbin.com/jigozuvuko
  2. デバウンス VS スロットル https://github.com /dcorb/デバウンススロットル

上記は JavaScript のスロットル関数 Throttle の詳細な説明です。さらに関連するコンテンツについては、PHP 中国語 Web サイト (www.php.cn) を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。