jquery - Limitador simple en JavaScript
Estoy buscando un limitador simple en JavaScript. Sé que librerías como lodash y underscore lo tienen, pero solo para una función sería excesivo incluir alguna de esas librerías.
También estaba comprobando si jQuery tiene una función similar, no pude encontrarla.
He encontrado un acelerador que funciona, y aquí está el código:
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);
}
};
}
El problema con esto es: dispara la función una vez más después de que se complete el tiempo de limitación. Así que supongamos que hice una limitación que se dispara cada 10 segundos al presionar una tecla: si presiono una tecla 2 veces, todavía se disparará la segunda pulsación de tecla cuando se completen los 10 segundos. No quiero este comportamiento.
Respuesta #1
Usaría el código fuente de underscore.js o lodash para encontrar una versión bien probada de esta función.
Aquí está la versión ligeramente modificada del código de underscore para eliminar todas las referencias a underscore.js en sí mismo:
// 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;
};
};
Tenga en cuenta que este código se puede simplificar si no necesita todas las opciones que soporta underscore.
Por favor, encuentre a continuación una versión muy simple y no configurable de esta función:
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);
}
}
}
Edición 1: Se eliminó otra referencia al guion bajo, gracias al comentario de @Zettam
Edición 2: Agregada sugerencia sobre lodash y posible simplificación del código, gracias al comentario de @lolzery @wowzery
Edición 3: Debido a solicitudes populares, agregué una versión muy simple, no configurable de la función, adaptada del comentario de @vsync
Respuesta #2
¿Qué hay de esto?
function throttle(func, timeFrame) {
var lastTime = 0;
return function () {
var now = Date.now();
if (now - lastTime >= timeFrame) {
func();
lastTime = now;
}
};
}
Simple.
Puede que le interese echar un vistazo a la fuente.
Respuesta #3
callback: toma la función que debe ser llamada
límite: número de veces que se debe llamar a la función dentro del límite de tiempo
tiempo: período de tiempo para reiniciar el conteo del límite
funcionalidad y uso: Supongamos que tienes una API que permite al usuario llamarla 10 veces en 1 minuto
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);Respuesta #4
Agregando a la discusión aquí (y para los visitantes más recientes), si la razón para no usar el casi de facto throttle de lodash es tener un paquete o bundle de tamaño más pequeño, entonces es posible incluir solo throttle en tu bundle en lugar de toda la biblioteca lodash. Por ejemplo, en ES6, sería algo así:
import throttle from 'lodash/throttle';
Además, hay un paquete de throttle solo de lodash llamado lodash.throttle que se puede usar con un simple import en ES6 o require en ES5.
Respuesta #5
Solo necesitaba una función de control/límite para el evento de redimensionamiento de la ventana, y por curiosidad, también quería saber qué son y cómo funcionan.
He leído múltiples publicaciones de blog y preguntas y respuestas en SO, pero todas parecen complicar demasiado esto, sugieren bibliotecas o simplemente proporcionan descripciones y no implementaciones simples en JS puro.
No proporcionaré una descripción ya que es abundante. Así que aquí está mi implementación:
function throttle(callback, delay) {
var timeoutHandler = null;
return function () {
if (timeoutHandler == null) {
timeoutHandler = setTimeout(function () {
callback();
timeoutHandler = null;
}, delay);
}
}
}
function debounce(callback, delay) {
var timeoutHandler = null;
return function () {
clearTimeout(timeoutHandler);
timeoutHandler = setTimeout(function () {
callback();
}, delay);
}
}
Estos podrían necesitar ajustes (por ejemplo, inicialmente la devolución de llamada no se llama de inmediato).
Vea la diferencia en acción (intente cambiar el tamaño de la ventana):
function throttle(callback, delay) {
var timeoutHandler = null;
return function () {
if (timeoutHandler == null) {
timeoutHandler = setTimeout(function () {
callback();
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
Respuesta #6
Aquí está cómo implementé la función throttle en ES6 en 9 líneas de código, espero que ayude
function throttle(func, delay) {
let timeout = null
return function(...args) {
if (!timeout) {
timeout = setTimeout(() => {
func.call(this, ...args)
timeout = null
}, delay)
}
}
}
Haz clic en este enlace para ver cómo funciona.
