Reorganizando los callbacks en jQuery

Una de las aspectos más interesantes de utilizar una librería -o framework- Javascript como jQuery, MooTools o Dojo es la posibilidad de asociar callbacks a prácticamente todos los métodos disponibles.

NOTA: Un callback es la acción que se ejecuta inmediatamente después de completarse la acción que lo implementa y que, por lo general, se inicia con una llamada a una función.

Método tradicional

En jQuery, una estructura básica de callback podría ser la siguiente:

$('#myButton').click( function(){
  // Do something...
} );

El callback se corresponde en este caso con una función anónima que se encarga de realizar alguna acción específica una vez disparado el evento click.

Este método, es el que podemos encontrar tanto en la documentación oficial de la librería como en la gran mayoría de sus plugins que integran esta funcionalidad.

Método de callback asociado

De forma alternativa a la función anónima, podemos realizar una llamada a otra función definida en alguna otra parte de nuestro código:

$('#myButton').click( myFunction );
 
function myFunction( event ){
  // Do something...
}

En este caso, invocamos a una función que recibe como parámetro todo el objeto correspondiente al evento que la ha disparado.

NOTA: Hay que tener en cuenta que en el callback, estamos indicamos el nombre de la función a la que queremos saltar y no invocándola directamente por lo que no hay que incluir los paréntesis.

Este modelo permite, por ejemplo, asociar un mismo callback a varios eventos diferentes, lo que permitiría una estructura DRY fácilmente mantenible.

Inconvenientes de los métodos clásicos

Los dos métodos anteriores, pese a que son los más comunes, presentan un par de problemas.

El tradicional tiene el incoveniente de que, una vez asignada una función de respuesta, es difícil de desasociar. En un escenario complejo, es posible que no queramos mantener siempre las mismas acciones por ejemplo tras pulsar un botón. Una solución sería aplicar un ‘unbind‘ al elemento, pero seguidamente tendríamos que volver a asociar un nuevo evento ‘click’ para indicar las nuevas acciones.

$('#myButton').click( function(){
  // Do something...
} );
 
// ... More code
 
// New callback
$('#myButton').unbind( 'click' ).click( function(){
  // Do other thing...
} );

El resultado es un código complejo de seguir y muy desorganizado. En este caso, la llamada a una función definida puede resultar más interesante.

Pero con respecto a este segundo método, el problema que podemos encontrar es de organización: varios eventos que llaman a varias funciones definidas suponen muchas estructuras de bloque dispersas por el código. Localizarlas para realizar modificaciones puede volverse incómodo en aplicaciones con varios cientos de líneas de código.

Método alternativo (y organizado)

Consideremos la siguiente estructura:

$("#myButton").click( callbacks.myFunctionOne );
$("#otherButton").click( callbacks.myFunctionTwo );
 
// We write all callbacks in the same object
var callbacks = {
 
  myFunctionOne: function( event ) {
    // Do something...
  },
 
  myFunctionTwo: function( event ) {
    // Do something...
  }
 
}

Con este patrón, tendremos todas las respuestas agrupadas en un mismo objeto fácil de localizar y mantener.

Además, a la hora de desasociar una acción determinada, basta con un ‘unbind‘ sobre la función en cuestión:

$("#someid").unbind( 'click', callbacks.myFunctionOne );

Conclusión

Agrupando todos nuestros callbacks en un objeto independiente, conseguimos un código más legible y mantenible, además de que facilitamos la tarea de desasociar acciones en aquellos eventos que lo precisen en tiempo de ejecución.

Acerca de Carlos Benítez

Programador Web y arquitecto Javascript. Fundador de www.etnassoft.com y OpenLibra, la Biblioteca Libre Online
Esta entrada fue publicada en Javascript, jQuery y etiquetada , , , , . Guarda el enlace permanente. Sígueme en Twitter

Últimos libros gratuitos añadidos a OpenLibra

{5} Comentarios.

  1. 29Mar2011/
    13:14 h /
    inyaka

    excelente, es de esta forma como se agrega complejidad innecesaria al codigo, dime si tienes 100 eventos en una pagina con 100 funciones principales + 300 funciones auxiliares
    ¿como vas a saber que funcion esta siendo disparada? si solo ves una variable
    ¿no seria mas facil ver el evento y luego buscar la funcion? recuerda que lo editores modernos listan las funciones

    • 29Mar2011/
      13:31 h /

      Hola;
      no he entendido bien tu comentario. Creo que quieres decir que es más fácil poner el nombre de la función y luego buscarla por el código usando las herramientas del IDE.
      Es una solución correcta, pero los códigos tienen que estar organizados independientemente de la herramienta que luego utilices para editarlo. El notepad o el gedit no listan funciones, ni un emacs o vi a pelo desde el shell tampoco: ten en cuenta que hay veces que tenemos que modificar aplicaciones a través de SSH y utilizando solo la consola.

      Pero en general, la idea es que precisamente en aplicaciones con 100 eventos y sus respectivos callbacks, estén todos agrupados dentro de un mismo objeto. Es más fácil de mantener y más legible para los desarrolladores que vengan detrás.

      Pero solo es una cuestión de organización; si nos sentimos más cómodos llamando únicamente a la función o codificando el callback directamente en el evento, no hay razón para cambiar.
      Saludos!

  2. 01Abr2011/
    22:36 h /

    todo sería mucho más sencillo si hubiese una manera sencilla de implementar el singleton en javascript o incluso métodos estáticos.

    Yo, personalmente, prefiero una salida un poco más horrorosa, pero efectiva; crear una clase con los métodos de Callback y una instancia global de esa clase. De esta manera, los callbacks están identificados por una instancia de clase y sus respectivos métodos. Adicionalmente, el código queda un poco más limpio que con variables y yo mantengo la ilusión subjetiva de que sigo trabajando con clases y no con código funcional en modo spaguetti :-DDDD

    Gracias por darme el empujón inicial para aventurarme a encontrar una solución que me agrade.

    en cuanto a la identificación de qué se inioca y cómo, cero que está bastante claro (y más cuando hay llamadas recursivas) que esto:

    $.get("/index.php", { r: 'ftg/create' },GLOBALCallbacks.getCreate);
    

    que esto:

    $.get("/index.php", { r: 'ftgPiece/create' }, function(){
    //somestuff
            //other callback function (data)....
    }
    );
    

    el tema de depender del IDE no lo veo necesario, si usas los callbacks con objetos.

  3. 04Ago2011/
    12:45 h /
    j4xl

    Gracias por el articulo, estoy un poco verde con estos temas aun. Una pagina bastante interesante, incluida también OpenLibra.

    He estado siguiendo tus consejos y he decido probar a implementar mis callbacks siguiendo el método de la variable, haciendo pruebas no me funcionaba (¿he dicho que estoy verde ya?) y tras comerme el tarro buscando como un idiota, me he dado cuenta de que faltaban las comas para separar los distintos métodos/funciones en la inicialización del array/objeto.

    Lo comento porque, quizá venga otro detrás mio y vuelva a encontrarse con este estúpido inconveniente, y podrías solucionarlo corrigiendo el ejemplo.

    [code]
    $(“#myButton”).click( callbacks.myFunctionOne );
    $(“#otherButton”).click( callbacks.myFunctionTwo );

    // We write all callbacks in the same object
    var callbacks = {

    myFunctionOne: function( event ) {
    // Do something…
    }, //<– Aquí la maldita coma!

    myFunctionTwo: function( event ) {
    // Do something…
    }
    }
    [/code]

    Gracias de nuevo!

    PD: He llegado aquí a través del twitter de "ender wiggins" @fred_SSC, me congratula ver que también hay comentarios suyos! XD

    • 04Ago2011/
      12:55 h /

      Corregido!! Olvidé la coma y tienes razón: si no se domina el tema el error puede pasar inadvertido.

      Gracias por el reporte!

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Licencia Creative Commons 3.0

®Copyright 2016. Cotenido web bajo licencia Creative Commons 3.0

Códigos bajo licencias MIT y GPL. Ver página de licencias para más información