sábado, 19 de enero de 2013

Debounced Functions

John Hann, en una entrada en su blog propone un concepto muy interesante que llama debouncing. El termino no tiene una traducción exacta en español, sería algo así como eliminar rebotes. Es un término sacado de los circuitos electromecánicos. Cuando un contacto se cierra se producen micro-rebotes (bounce significa 'rebotar') que hacen que el circuito esté cerrado/abierto varias veces antes de que se cierre definitivamente. Estas múltiples señales deben eliminarse para enviar una sola, de circuito cerrado cuando el estado es estable.

Se envía una sola señal cuando el estado se ha estabilizado


En el mundo JavaScript el concepto se aplica para funciones que queremos que se ejecuten una sóla vez aunque el evento que las dispara se lance cientos de veces en un determinado periodo.

Un ejemplo sencillo de uso, sacado del blog de Paul Irish, sería un callback para el evento resize. Firefox lanza sólo un evento cuando el usuario ha terminado de redimensionar la ventana, pero los demás navegadores lanzan eventos continuamente mientras la ventana se está redimensionado. Para ejecutar la función sólo una vez en todos los casos, podemos llamar a un debounced callback  que sólo se ejecutará una vez.

Básicamente se trata de que durante un determinado lapso de tiempo (configurable) se absorban todas las llamadas a la función y se ejecute sólo una. La función sólo se lanza cuando las llamadas se han estabilizado durante este lapso de tiempo, es decir una vez que ha pasado el tiempo fijado y no se ha producido una nueva llamada.

Es muy útil para eventos relacionados con el arrastre del ratón, que se disparan continuamente mientras el ratón se está moviendo.

Podemos crear una utilidad que nos devuelva una versión 'debounced' de una función cualquiera. Esta sería la función, sacada del ejemplo de Paul Irish que comentaba antes:
  var debounce = function (func, threshold, execAsap) {
      var timeout;
      return function debounced () {
          var obj = this, args = arguments;
          function delayed () {
              if (!execAsap)
                  func.apply(obj, args);
              timeout = null; 
          };
 
          if (timeout)
              clearTimeout(timeout);
          else if (execAsap)
              func.apply(obj, args);
 
          timeout = setTimeout(delayed, threshold || 100); 
      };
  }
Los parámetros son los siguientes:
  • func: La función original que queremos convertir en 'debounced' 
  • threshold: El lapso de tiempo, en ms, durante el que tienen que estabilizarse las llamadas ( no producirse ninguna nueva). Es opcional, 100ms es el valor por defecto. 
  • execAsap: Opcional. Por defecto la función se ejecuta al final del periodo de estabilización, con este parámetro a *true* la función se ejecuta inmediatamente, antes del periodo de estabilización

Si, por ejemplo, tenemos una función showMessage y queremos usarla como debounced callback para el evento resize, podemos hacer:

$(window).resize( debounce( showMessage) );


No hay comentarios:

Publicar un comentario