Artículo original escrito por Ondrej Polesny
Artículo original Debounce – How to Delay a Function in JavaScript (JS ES6 Example)
Traducido y adaptado por Josué

En JavaScript, una función debounce se asegura de que su código sólo se active una vez por cada entrada del usuario. Las sugerencias de los cuadros de búsqueda, el autoguardado de los campos de texto y la eliminación de los clics dobles son casos de uso de la función debounce.

En este tutorial, aprenderemos a crear una función debounce en JavaScript.

¿Qué es debounce?

El término debounce proviene de la electrónica. Cuando se pulsa un botón, por ejemplo, en el mando a distancia del televisor, la señal viaja hasta el microchip del mando con tanta rapidez que, antes de que consiga soltar el botón, rebota y el microchip registra su "clic" varias veces.

debounce-button

Para mitigar esto, una vez que se recibe una señal del botón, el microchip deja de procesar las señales del botón durante unos microsegundos mientras es físicamente imposible que lo vuelva a pulsar.

Debounce en JavaScript

En JavaScript, el caso de uso es similar. Queremos activar una función, pero sólo una vez por caso de uso.

Digamos que queremos mostrar sugerencias para una consulta de búsqueda, pero sólo después de que el visitante haya terminado de escribirla.

O queremos guardar los cambios en un formulario, pero sólo cuando el usuario no esté trabajando activamente en esos cambios, ya que cada "guardado" nos cuesta un viaje a la base de datos.

Y mi favorito: algunos se acostumbraron mucho a Windows 95 y ahora hacen doble clic en todo ?.

Esta es una simple implementación de la función debounce (CodePen aquí):

function debounce(func, timeout = 300){
  let timer;
  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => { func.apply(this, args); }, timeout);
  };
}
function saveInput(){
  console.log('Saving data');
}
const processChange = debounce(() => saveInput());

Se puede utilizar en un input:

<input type="text" onkeyup="processChange()" />

O en un botón:

<button onclick="processChange()">Click me</button>

O un evento en la ventana:

window.addEventListener("scroll", processChange);

Y en otros elementos como una simple función JS.

¿Qué ocurre aquí? El debounce es una función especial que se encarga de dos tareas:

  • Asignación de un ámbito para la variable del temporizador.
  • Programar la función para que se active en un momento determinado.

Vamos a explicar cómo funciona esto en el primer caso de uso con la entrada de texto.

Cuando un visitante escribe la primera letra y suelta la tecla, el debounce primero reinicia el temporizador con clearTimeout(timer). En este punto, el paso no es necesario, ya que no hay nada programado todavía. Luego programa la función proporcionada -saveInput()- para ser invocada en 300 ms.

Pero digamos que el visitante sigue escribiendo, por lo que cada liberación de la tecla desencadena el debounce de nuevo. Cada invocación necesita reiniciar el temporizador, o, en otras palabras, cancelar los planes anteriores con saveInput(), y reprogramarlo para un nuevo tiempo-300 ms en el futuro. Esto continúa mientras el visitante siga pulsando las teclas por debajo de los 300 ms.

La última programación no se borrará, por lo que finalmente se llamará a saveInput().

Al revés: ¿Cómo ignorar los acontecimientos posteriores?

Eso está bien para activar el autoguardado o mostrar sugerencias. ¿Pero qué pasa con el caso de uso con múltiples clics de un solo botón? No queremos esperar al último clic, sino registrar el primero e ignorar el resto (CodePen Aquí).

function debounce_leading(func, timeout = 300){
  let timer;
  return (...args) => {
    if (!timer) {
      func.apply(this, args);
    }
    clearTimeout(timer);
    timer = setTimeout(() => {
      timer = undefined;
    }, timeout);
  };
}

Aquí activamos la función saveInput() en la primera llamada debounce_leading causada por el primer clic del botón. Programamos la destrucción del temporizador para 300 ms. Cada clic de botón posterior dentro de ese marco de tiempo ya tendrá el temporizador definido y sólo empujará la destrucción 300 ms hacia el futuro.

Implementaciones de debounce en las librerías

En este artículo, te mostré cómo implementar una función de debounce en JavaScript y utilizarla para, bueno, rebotar eventos desencadenados por elementos del sitio web.

Sin embargo, no es necesario que uses tu propia implementación de debounce en tus proyectos si no quieres. Librerías JS ampliamente utilizadas ya contienen su implementación. Aquí hay algunos ejemplos:

LibraryExample
jQuery (via library)$.debounce(300, saveInput);
Lodash_.debounce(saveInput, 300);
Underscore_.debounce(saveInput, 300);