Underscore: 提案:_。debounceと_.throttleは、引数を組み合わせる方法について追加のパラメーターを取ります

作成日 2011年09月22日  ·  11コメント  ·  ソース: jashkenas/underscore

_.debounce()を使用してデバウンスされた関数を作成し、それを3つの異なる引数のセットで3回続けて呼び出すと、(v1.1.7以降)ラップされたペイロード関数は、 3番目の呼び出し-つまり、最初と2番目の引数は破棄されます。

これはしばしば有効ですが(そしてキーのデバウンスが通常行うこと、したがって妥当なデフォルト)、引数を蓄積するためにデバウンスを使用したいと思っています。たとえば、一度に複数のキーを取得できるAJAX呼び出しがあるので、デバウンスを使用してバッファリングしますキーを1秒間上げてから、結合されたリクエストを発行します。

したがって、私の提案は、debounceは、2つの引数で呼び出されるオプションの3番目の「combine」引数を取ることです。

  • 1つ目は、これまでに蓄積された引数のリストです(おそらく未定義-つまり、最初の呼び出しでリストがありません)
  • 2番目は最新の呼び出しの引数のリストです(おそらく空のリスト)
    累積された引数の新しいリストを返します。 ペイロード関数が呼び出されると、蓄積された引数のリストがクリアされます。

Combineパラメータに値が渡されない場合、デフォルトのcombineは既存の動作を保持します
function(acc, newargs) { return newargs; }
ただし、最初の引数セットを使用することもできます
function(acc, newargs) { return acc || newargs; }
または私がやりたいことは、単にすべての引数を追加することです
function(acc,newargs) { return (acc || []).concat(newargs); }
そしてもちろん、他の人はもっと素晴らしいことをしたいと思うかもしれません

これには、内部制限機能に次の変更が必要になります

  // Internal function used to implement `_.throttle` and `_.debounce`.
  var limit = function(func, wait, debounce, combine) {
    var timeout, allargs;
    return function() {
      var context = this;
      allargs = combine(allargs,  slice.call(arguments,0))
      var throttler = function() {
        timeout = null;
        var args = allargs;
        allargs = undefined;
        func.apply(context, args);
      };
      if (debounce) clearTimeout(timeout);
      if (debounce || !timeout) timeout = setTimeout(throttler, wait);
    };
  };

次に、デバウンスに変更して、指定されていない場合はデフォルト値で新しい引数を受け入れて渡します。

  _.debounce = function(func, wait, combine) {
    return limit(func, wait, true, combine || function(acc,newargs) { return newargs; });
  };

対応するスロットル関数は現在、最初の引数セットのみを使用します(スロットルは、最初の呼び出しから待機ミリ秒以内に発生する呼び出しを効果的に無視し、引数の最初の呼び出しセットを使用します。デバウンスは、待機期間内に発生するシーケンスの最後の呼び出しを除くすべてを効果的に無視します。お互いの)なので、現在のデフォルトの動作を再び維持するために、以下をお勧めします

  _.throttle = function(func, wait, combine) {
    return limit(func, wait, false, combine || function(acc,newargs) { return acc || newargs; });
  };

これは、引数リストを維持するための過度のラッパーなしでこの機能を実現するための最も簡単で一般的な方法のように思われますが、アンダースコアを変更せずにこれを実現する簡単な方法があるかどうかを知りたいと思います。

enhancement wontfix

最も参考になるコメント

OK、続けないでください、しかし誰かが後でこれを見て同じことをする方法を疑問に思っている場合、私はこれがデバウンス自体を変更せずに最もクリーンな方法であると考えました(私はそれを_オブジェクトに追加します、他の人は好まないかもしれませんに)

_.mixin({
  debounceReduce: function(func, wait, combine) {
    var allargs,
        context,
        wrapper = _.debounce(function() {
            var args = allargs;
            allargs = undefined;
            func.apply(context, args);
        }, wait);
        return function() {
            context = this;
            allargs = combine.apply(context, [allargs,  Array.prototype.slice.call(arguments,0)]);
            wrapper();
        };
    }
})

これにより、コンバイン関数によって引数が削減されたデバウンスされた関数が得られます。たとえば、

  delayLog = _.debounceReduce(function() { console.log(arguments); }, 5000, 
                              function(acc,args) { return (acc || []).concat(args); });
  delayLog(3,4);
  delayLog(7,8,9);

数秒後に、配列[3,4,7,8,9]を使用してconsole.logを呼び出します。

全てのコメント11件

私自身の提案にコメントすると、combine()の呼び出しは、ペイロード関数と同じコンテキストを指定する必要があります。

allargs = combine.apply(this, [allargs, slice.call(arguments,0)])

引数がコンテキストオブジェクトにアクセスする必要がある場合に備えて...

マスターで修正する必要があります。 throttleは、常に引数の最新のコピーを使用し、すぐに1回起動し、その後N秒ごとに起動するという正しい動作を示す必要があります...そしてN秒後にリセットされます最後のトレーリングトリガーが発生しました。

このリクエストによって提起された問題はマスターにも当てはまるので、あなたの密接なコメントは別の問題(おそらく#170)にも当てはまると思います。
結合されている呼び出しからデバウンスまたはスロットルに引数を蓄積させる簡単な方法はまだありません。オプションの結合引数が指定されていない場合でも、デフォルトの動作を変更しない便利なオプションの追加だと思います。

ああ、その通りです。 引数の累積はアンダースコアの範囲外です。蓄積されたデータは、 _.throttleおよび_.debounce関数の外部の適切な場所に自由に保管してください。

それは残念です、私はデバウンスがタイムアウトを伴う複数の呼び出しに対する一種の左折(削減)であると考えています、したがってアキュムレータ...しかしそれはあなたの呼び出しです:)

OK、続けないでください、しかし誰かが後でこれを見て同じことをする方法を疑問に思っている場合、私はこれがデバウンス自体を変更せずに最もクリーンな方法であると考えました(私はそれを_オブジェクトに追加します、他の人は好まないかもしれませんに)

_.mixin({
  debounceReduce: function(func, wait, combine) {
    var allargs,
        context,
        wrapper = _.debounce(function() {
            var args = allargs;
            allargs = undefined;
            func.apply(context, args);
        }, wait);
        return function() {
            context = this;
            allargs = combine.apply(context, [allargs,  Array.prototype.slice.call(arguments,0)]);
            wrapper();
        };
    }
})

これにより、コンバイン関数によって引数が削減されたデバウンスされた関数が得られます。たとえば、

  delayLog = _.debounceReduce(function() { console.log(arguments); }, 5000, 
                              function(acc,args) { return (acc || []).concat(args); });
  delayLog(3,4);
  delayLog(7,8,9);

数秒後に、配列[3,4,7,8,9]を使用してconsole.logを呼び出します。

@schmerg —これは非常に便利に見えます。 MITライセンスの下でそのコードのライセンスを取得してもよろしいですか? (「はい」で十分です!)

@markjaquith確かに-はい。 喜んで...

誰かがやって来て、上記の最新のjsバージョンを更新/コメントしたい場合:

_.mixin({
  debounceReduce(func, wait, combine) {
    let allArgs; // accumulator for args across calls

    // normally-debounced fn that we will call later with the accumulated args
    const wrapper = _.debounce(() => func(allArgs), wait);

    // what we actually return is this function which will really just add the new args to
    // allArgs using the combine fn
    return (...args) => {
      allArgs = combine(allArgs,  [...args]);
      wrapper();
    };
  },
});

@kmannislandsねえ、あなたのバージョンはwrapper() allArgsをリセットしないので、 func()への後続の呼び出しは、現在のバッチだけでなく、引数の履歴バッチも取得します。

すべきではありません:

const wrapper = _.debounce(() => {
    const args = allArgs;
    allArgs = undefined;
    func(args);
}, wait);

@markjaquith +1

またfunc( args )は、 func.apply(context, args);を使用する元のバージョンとは異なります。
前者の場合、 argsはターゲットfunc()でそのまま使用されますが、後者の_(元のコード)_では、通常の関数でargumentsのいずれかを使用する必要がありますまたはes6ファットアロー関数の( ...args )

このページは役に立ちましたか?
0 / 5 - 0 評価