JavaScript でシンプルなスロットルを探しています。lodash や underscore などのライブラリにはスロットルがあることは知っていますが、1 つの関数だけなので、これらのライブラリのいずれかを含めるのはやりすぎです。
jQuery に同様の機能があるかどうかも確認しましたが、見つかりませんでした。
機能するスロットルが1つ見つかりましたコードは次のとおりです。
function throttle(fn, threshhold, scope) {
threshhold || (threshhold = 250);
var last,
deferTimer;
return function () {
var context = scope || this;
var now = +new Date,
args = arguments;
if (last && now < last + threshhold) {
// hold on to it
clearTimeout(deferTimer);
deferTimer = setTimeout(function () {
last = now;
fn.apply(context, args);
}, threshhold);
} else {
last = now;
fn.apply(context, args);
}
};
}
これの問題は、スロットル時間が完了した後に関数がもう一度実行されることです。したがって、キーを押すたびに 10 秒ごとに実行されるスロットルを作成したと仮定します。キーを 2 回押すと、10 秒が完了したときに 2 回目のキー押下が実行されます。この動作は望ましくありません。
ベストアンサー1
私はアンダースコアまたはロダッシュこの関数の十分にテストされたバージョンを見つけるには、ソース コードを参照してください。
以下は、underscore.js 自体へのすべての参照を削除するために、アンダースコア コードをわずかに変更したバージョンです。
// Returns a function, that, when invoked, will only be triggered at most once
// during a given window of time. Normally, the throttled function will run
// as much as it can, without ever going more than once per `wait` duration;
// but if you'd like to disable the execution on the leading edge, pass
// `{leading: false}`. To disable execution on the trailing edge, ditto.
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 : Date.now();
timeout = null;
result = func.apply(context, args);
if (!timeout) context = args = null;
};
return function() {
var now = Date.now();
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;
};
};
アンダースコアのサポートするすべてのオプションが必要ない場合は、このコードを簡略化できることに注意してください。
以下に、この関数の非常にシンプルで構成不可能なバージョンを示します。
function throttle (callback, limit) {
var waiting = false; // Initially, we're not waiting
return function () { // We return a throttled function
if (!waiting) { // If we're not waiting
callback.apply(this, arguments); // Execute users function
waiting = true; // Prevent future invocations
setTimeout(function () { // After a period of time
waiting = false; // And allow future invocations
}, limit);
}
}
}
編集 1: アンダースコアへの別の参照を削除しました。@Zettam のコメントに感謝します。
編集2: lodash とコードの簡素化の可能性についての提案を追加しました。@lolzery @wowzery のコメントに感謝します。
編集3: リクエストが多かったため、@vsyncのコメントを参考に、非常にシンプルで設定不可能なバージョンの関数を追加しました。