Ich suche nach einem einfachen Gas in JS. Ich weiß, dass Bibliotheken wie lodash und unterstreichen es haben, aber nur für eine Funktion ist es übertrieben, eine dieser Bibliotheken einzuschließen.
Ich habe auch geprüft, ob jquery eine ähnliche Funktion hat - konnte nicht finden.
Ich habe einen funktionierenden Gashebel gefunden , und hier ist der Code:
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);
}
};
}
Das Problem dabei ist: Es löst die Funktion nach Ablauf der Drosselzeit erneut aus. Nehmen wir also an, ich habe eine Drosselklappe gedrückt, die alle 10 Sekunden beim Drücken der Taste ausgelöst wird. Wenn ich zweimal die Taste drücke, wird der zweite Tastendruck nach 10 Sekunden immer noch ausgelöst. Ich möchte dieses Verhalten nicht.
javascript
jquery
Mia
quelle
quelle
Antworten:
Ich würde den Quellcode underscore.js oder lodash verwenden, um eine gut getestete Version dieser Funktion zu finden.
Hier ist die leicht modifizierte Version des Unterstrichcodes, um alle Verweise auf underscore.js selbst zu entfernen:
// 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; }; };
Bitte beachten Sie, dass dieser Code vereinfacht werden kann, wenn Sie nicht alle Optionen benötigen, die die Unterstützung unterstreichen.
Nachfolgend finden Sie eine sehr einfache und nicht konfigurierbare Version dieser Funktion:
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); } } }
Bearbeiten 1: Ein weiterer Verweis auf den Unterstrich wurde entfernt, danke an @Zettams Kommentar
Bearbeiten 2: Vorschlag zu lodash und möglicher Code-Vereinfachung hinzugefügt, danke an den Kommentar von @lolzery @wowzery
Bearbeiten 3: Aufgrund beliebter Anfragen habe ich eine sehr einfache, nicht konfigurierbare Version der Funktion hinzugefügt, die aus dem Kommentar von @vsync übernommen wurde
quelle
arguments
Objekt wird immer in jeder Funktion definiert, die keine Pfeilfunktion ist: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…Rückruf : Übernimmt die Funktion, die aufgerufen werden soll
limit : Häufigkeit, mit der diese Funktion innerhalb des Zeitlimits aufgerufen werden soll
Zeit : Zeitspanne zum Zurücksetzen der Grenzwertanzahl
Funktionalität und Verwendung : Angenommen, Sie haben eine API, mit der der Benutzer sie 10 Mal in 1 Minute aufrufen kann
function throttling(callback, limit, time) { /// monitor the count var calledCount = 0; /// refresh the `calledCount` varialbe after the `time` has been passed setInterval(function(){ calledCount = 0 }, time); /// creating a closure that will be called return function(){ /// checking the limit (if limit is exceeded then do not call the passed function if (limit > calledCount) { /// increase the count calledCount++; callback(); /// call the function } else console.log('not calling because the limit has exceeded'); }; } //////////////////////////////////////////////////////////// // how to use /// creating a function to pass in the throttling function function cb(){ console.log("called"); } /// calling the closure function in every 100 milliseconds setInterval(throttling(cb, 3, 1000), 100);
quelle
callback(...arguments)
um die ursprünglichen Argumente beizubehalten. sehr praktischsetInterval
für Zwecke, die nicht beabsichtigt waren.Was ist damit?
function throttle(func, timeFrame) { var lastTime = 0; return function () { var now = new Date(); if (now - lastTime >= timeFrame) { func(); lastTime = now; } }; }
Einfach.
Vielleicht möchten Sie einen Blick auf die Quelle werfen .
quelle
Date.now()
anstattnew Date()
Wenn Sie der Diskussion hier (und für neuere Besucher) hinzufügen, dass der Grund dafür, dass Sie das fast de facto
throttle
von nicht verwenden, darinlodash
besteht, ein kleineres Paket oder Bundle zu haben, ist es möglich, nurthrottle
Ihr Bundle anstelle der gesamtenlodash
Bibliothek aufzunehmen. Zum Beispiel in ES6 wäre es so etwas wie:import throttle from 'lodash/throttle';
Es gibt auch ein
throttle
einziges Paket vonlodash
aufgerufenlodash.throttle
, das mit einem einfachenimport
in ES6 oderrequire
in ES5 verwendet werden kann.quelle
debounce
undisObject
die gesamte Bundle-Größe wird auf ca. 2,1 KB reduziert . Ich nehme an, macht für ein kleines Programm keinen Sinn, aber ich würde es vorziehen, es in größeren Projekten zuIch brauchte nur eine Drossel- / Entprellungsfunktion für Fenstergrößenänderungsereignisse, und da ich neugierig war, wollte ich auch wissen, was diese sind und wie sie funktionieren.
Ich habe mehrere Blog-Beiträge und QAs zu SO gelesen, aber alle scheinen dies zu komplizieren, Bibliotheken vorzuschlagen oder nur Beschreibungen und keine einfachen JS-Implementierungen bereitzustellen.
Ich werde keine Beschreibung geben, da es reichlich ist. Hier ist meine Implementierung:
function throttle(callback, delay) { var timeoutHandler = null; return function () { if (timeoutHandler == null) { timeoutHandler = setTimeout(function () { callback(); clearInterval(timeoutHandler); timeoutHandler = null; }, delay); } } } function debounce(callback, delay) { var timeoutHandler = null; return function () { clearTimeout(timeoutHandler); timeoutHandler = setTimeout(function () { callback(); }, delay); } }
Diese müssen möglicherweise angepasst werden (z. B. wird der Rückruf anfangs nicht sofort aufgerufen).
Sehen Sie den Unterschied in der Aktion (versuchen Sie, die Größe des Fensters zu ändern):
Code-Snippet anzeigen
function throttle(callback, delay) { var timeoutHandler = null; return function () { if (timeoutHandler == null) { timeoutHandler = setTimeout(function () { callback(); clearInterval(timeoutHandler); timeoutHandler = null; }, delay); } } } function debounce(callback, delay) { var timeoutHandler = null; return function () { clearTimeout(timeoutHandler); timeoutHandler = setTimeout(function () { callback(); }, delay); } } var cellDefault = document.querySelector("#cellDefault div"); var cellThrottle = document.querySelector("#cellThrottle div"); var cellDebounce = document.querySelector("#cellDebounce div"); window.addEventListener("resize", function () { var span = document.createElement("span"); span.innerText = window.innerWidth; cellDefault.appendChild(span); cellDefault.scrollTop = cellDefault.scrollHeight; }); window.addEventListener("resize", throttle(function () { var span = document.createElement("span"); span.innerText = window.innerWidth; cellThrottle.appendChild(span); cellThrottle.scrollTop = cellThrottle.scrollHeight; }, 500)); window.addEventListener("resize", debounce(function () { var span = document.createElement("span"); span.innerText = window.innerWidth; cellDebounce.appendChild(span); cellDebounce.scrollTop = cellDebounce.scrollHeight; }, 500));
table { border-collapse: collapse; margin: 10px; } table td { border: 1px solid silver; padding: 5px; } table tr:last-child td div { width: 60px; height: 200px; overflow: auto; } table tr:last-child td span { display: block; }
<table> <tr> <td>default</td> <td>throttle</td> <td>debounce</td> </tr> <tr> <td id="cellDefault"> <div></div> </td> <td id="cellThrottle"> <div></div> </td> <td id="cellDebounce"> <div></div> </td> </tr> </table>
JSFiddle
quelle
Hier ist, wie ich die Drosselfunktion in ES6 in 9LOC implementiert habe. Ich hoffe, es hilft
function throttle(func, delay) { let timeout = null return function(...args) { if (!timeout) { timeout = setTimeout(() => { func.call(this, ...args) timeout = null }, delay) } } }
Klicken Sie darauf Link , um zu sehen, wie es funktioniert.
quelle
...
Spread-Syntax unangemessen, da immer nur ein Argument an den Ereignis-Listener übergeben wird: das Ereignisobjekt.Ich habe ein npm-Paket mit einigen Drosselfunktionen erstellt:
throttleAndQueue
Gibt eine Version Ihrer Funktion zurück, die höchstens alle W Millisekunden aufgerufen werden kann, wobei W wartet. Anrufe an Ihre Funk, die häufiger als W auftreten, werden in die Warteschlange gestellt, um alle W ms aufgerufen zu werden
throttledUpdate
Gibt eine Version Ihrer Funktion zurück, die höchstens alle W Millisekunden aufgerufen werden kann, wobei W wartet. Bei Anrufen, die häufiger als W stattfinden, ist der letzte Anruf der angerufene (der letzte hat Vorrang).
drosseln
begrenzt Ihre Funktion so, dass sie höchstens alle W Millisekunden aufgerufen wird, wobei W wartet. Anrufe über W werden abgebrochen
quelle
Für diesen Zweck gibt es eine Bibliothek, Backburner.js von Ember.
https://github.com/BackburnerJS/
Du würdest es so benutzen.
var backburner = new Backburner(["task"]); //You need a name for your tasks function saySomething(words) { backburner.throttle("task", console.log.bind(console, words) }, 1000); } function mainTask() { "This will be said with a throttle of 1 second per word!".split(' ').map(saySomething); } backburner.run(mainTask)
quelle
Diese Drosselfunktion basiert auf ES6. Rückruffunktionen akzeptieren Argumente (Argumente) und funktionieren dennoch mit der Drosselfunktion. Sie können die Verzögerungszeit ganz nach Ihren App-Anforderungen anpassen. Für den Entwicklungsmodus wird 1 Mal pro 100 ms verwendet. Das Ereignis "oninput" ist nur ein Beispiel für den häufigen Fall seiner Verwendung:
const callback = (...args) => { console.count('callback throttled with arguments:', args); }; throttle = (callback, limit) => { let timeoutHandler = 'null' return (...args) => { if (timeoutHandler === 'null') { timeoutHandler = setTimeout(() => { callback(...args) timeoutHandler = 'null' }, limit) } } } window.addEventListener('oninput', throttle(callback, 100));
PS Wie @Anshul erklärte: Durch die Drosselung wird eine maximale Anzahl von Aufrufen einer Funktion im Laufe der Zeit erzwungen. Wie in "Führen Sie diese Funktion höchstens einmal alle 100 Millisekunden aus."
quelle
Versuchen Sie im folgenden Beispiel, mehrmals auf die Schaltfläche zu klicken, aber die
myFunc
Funktion wird nur einmal in 3 Sekunden ausgeführt. Die Funktionthrottle
wird mit der auszuführenden Funktion und der Verzögerung übergeben. Sie gibt einen Abschluss zurück, der in gespeichert istobj.throttleFunc
. Da nunobj.throttleFunc
ein Verschluss gespeichert wird,isRunning
bleibt der Wert von darin erhalten.function throttle(func, delay) { let isRunning; return function(...args) { let context = this; // store the context of the object that owns this function if(!isRunning) { isRunning = true; func.apply(context,args) // execute the function with the context of the object that owns it setTimeout(function() { isRunning = false; }, delay); } } } function myFunc(param) { console.log(`Called ${this.name} at ${param}th second`); } let obj = { name: "THROTTLED FUNCTION ", throttleFunc: throttle(myFunc, 3000) } function handleClick() { obj.throttleFunc(new Date().getSeconds()); }
button { width: 100px; height: 50px; font-size: 20px; }
<button onclick="handleClick()">Click me</button>
function throttle(func, delay) { let isRunning; return function() { if(!isRunning) { isRunning = true; func() setTimeout(function() { isRunning = false; }, delay); } } } function myFunc() { console.log('Called'); } let throttleFunc = throttle(myFunc, 3000); function handleClick() { throttleFunc(); }
button { width: 100px; height: 50px; font-size: 20px; }
<button onclick="handleClick()">Click me</button>
quelle
Unten ist das einfachste Gas, das ich mir vorstellen kann, in 13 LOC. Bei jedem Aufruf der Funktion wird eine Zeitüberschreitung erstellt und die alte abgebrochen. Die ursprüngliche Funktion wird erwartungsgemäß mit dem richtigen Kontext und den richtigen Argumenten aufgerufen.
function throttle(fn, delay) { var timeout = null; return function throttledFn() { window.clearTimeout(timeout); var ctx = this; var args = Array.prototype.slice.call(arguments); timeout = window.setTimeout(function callThrottledFn() { fn.apply(ctx, args); }, delay); } } // try it out! window.addEventListener('resize', throttle(function() { console.log('resize!!'); }, 200));
quelle
Hier ist meine eigene Version von Vikas Post:
throttle: function (callback, limit, time) { var calledCount = 0; var timeout = null; return function () { if (limit > calledCount) { calledCount++; callback(); } if (!timeout) { timeout = setTimeout(function () { calledCount = 0 timeout = null; }, time); } }; }
Ich finde, dass die Verwendung
setInterval
keine gute Idee ist.quelle
Ich möchte auch eine einfache Lösung vorschlagen, wenn es nur eine Funktion gibt, von der Sie wissen, dass Sie sie aufrufen werden (zum Beispiel: Suchen).
Hier ist, was ich in meinem Projekt gemacht habe
let throttle; function search() { if (throttle) { clearTimeout(throttle); } throttle = setTimeout(() => { sendSearchReq(str) }, 500); }
Die Suche wird beim Eingabeänderungsereignis aufgerufen
quelle
search()
Funktion wird das Timeout zurückgesetzt. Wenn ich also diesearch()
Funktion über jeder Millisekunde aufrufe, wird sie nursendSearchReq
einmal und dann nie wieder ausgeführt, anstatt alle 500 ms. Diese Funktion ist eher eine Verzögerung als eine Drossel.function throttle(targetFunc, delay){ let lastFunc; let lastTime; return function(){ const _this = this; const args = arguments; if(!lastTime){ targetFunc.apply(_this, args); lastTime = Date.now(); } else { clearTimeout(lastFunc); lastFunc = setTimeout(function(){ targetFunc.apply(_this, args); lastTime = Date.now(); }, delay - (Date.now() - lastTime)); } } }
Versuch es :
window.addEventListener('resize', throttle(function() { console.log('resize!!'); }, 200));
quelle
Einfache Gasfunktion -
Hinweis: Klicken Sie weiter auf die Schaltfläche. Das Konsolenprotokoll wird zuerst beim Klicken und dann erst alle 5 Sekunden angezeigt, bis Sie weiter klicken.
HTML -
<button id='myid'>Click me</button>
Javascript -
const throttle = (fn, delay) => { let lastTime = 0; return (...args) => { const currentTime = new Date().getTime(); if((currentTime - lastTime) < delay) { return; }; lastTime = currentTime; return fn(...args); } }; document.getElementById('myid').addEventListener('click', throttle((e) => { console.log('I am clicked'); }, 5000));
quelle
Wir können auch mit einem Flag implementieren.
var expensive = function(){ console.log("expensive functionnns"); } window.addEventListener("resize", throttle(expensive, 500)) function throttle(expensiveFun, limit){ let flag = true; return function(){ let context = this; let args = arguments; if(flag){ expensiveFun.apply(context, args); flag = false; setTimeout(function(){ flag = true; }, limit); } } }
quelle