¿Debounce o throttle?

Ambos limitan cuántas veces se ejecuta un handler. Elige según lo que el usuario deba ver mientras siguen llegando eventos.

Algunos eventos se disparan mucho más a menudo de lo necesario: input con cada tecla, scroll y pointermove decenas de veces por segundo. Debounce y throttle reducen el número de llamadas, pero se comportan de forma distinta mientras los eventos siguen llegando.

Debounce: esperar a que haya calma

Una función con debounce solo se ejecuta cuando los eventos se han detenido durante cierto tiempo. Encaja con trabajo que solo tiene sentido para el valor final, como enviar una búsqueda o guardar un borrador.

function debounce(fn, wait) {
  let timer;
  return function (...args) {
    clearTimeout(timer);
    timer = setTimeout(() => fn.apply(this, args), wait);
  };
}

input.addEventListener('input', debounce(search, 250));

Throttle: seguir, pero más despacio

Una función con throttle se ejecuta como mucho una vez por intervalo mientras los eventos continúan. Úsala cuando el usuario deba ver algún cambio durante la interacción, por ejemplo una indicación de posición mientras arrastra.

function throttle(fn, wait) {
  let last = 0;
  let timer;
  return function (...args) {
    const remaining = wait - (Date.now() - last);
    clearTimeout(timer);
    if (remaining <= 0) {
      last = Date.now();
      fn.apply(this, args);
    } else {
      timer = setTimeout(() => {
        last = Date.now();
        fn.apply(this, args);
      }, remaining);
    }
  };
}

Esta versión llama a la función de inmediato, después como mucho cada wait milisegundos y una vez más al final, para que el último valor nunca se pierda.

Para todo lo visual, la frecuencia de fotogramas

Si el handler solo actualiza lo que se ve en pantalla, el intervalo natural es un fotograma. requestAnimationFrame te da exactamente eso, sin tener que elegir un número:

let queued = false;
window.addEventListener('scroll', () => {
  if (queued) return;
  queued = true;
  requestAnimationFrame(() => {
    queued = false;
    updateProgressBar(window.scrollY);
  });
}, { passive: true });

Como regla rápida: debounce para el resultado, throttle para la respuesta visual y requestAnimationFrame para los píxeles.

← Todas las notas