Entendiendo Deferreds en jQuery
¿Qué es un Deferred?
Es un tipo de objeto que se introdujo en jQuery 1.5, que nos permite encadenar distintas acciones (callbacks) y disparar cierta funcionalidad (otros callbacks) dependiendo de su estado.
Es similar y persigue la misma idea de las Promises y/o Futures, ya también conocidas con anterioridad e implementadas por ejemplo como paquetes para nodejs, donde el asincronismo, el anidamiento y las dependencias entre callbacks incrementaban la complejidad del programa, y se necesitaba una forma de manejar esto de manera más limpia y mantenible.
Veamos Código
AJAX es uno de los principales usos de las deferreds en la mayoría de los casos. Un ejemplo sencillo:
Antes de la versión 1.5 de jQuery, así tendríamos que hacer para ejecutar dos acciones luego de la respuesta existosa a una llamada get:
$.get("postCopado.php",
function(data){
accionPostCarga1(data);
accionPostCarga2(data);
});
A partir de la 1.5, $.get nos devuelve un objeto que cumple con la interfaz de una Deferred, por lo que podemos hacer algo así:
// accionPostCarga1 y accionPostCarga2 son funciones que se van a ejecutar una vez que tengamos
// una respuesta exitosa a la petición get
$.get("postCopado.php").done(accionPostCarga1, accionPostCarga2);
Tanto como $.get como .done en este caso devuelven la misma Deferred ( o sea que es encadenable / chainable ) ==> podemos seguir agregando acciones:
// Agrego una función a ejecutar en caso de que la request get falle.
$.get("postCopado.php").done(accionPostCarga1, accionPostCarga2).fail(mostrarError);
O tambien puedo guardar la deferred en una variable y manipularla:
var req = $.get("postCopado.php");
req.done(accionPostCarga1, accionPostCarga2);
req.fail(mostrarError);
// ocultarAjaxLoaderIcon se va a ejecutar no importa si la respuesta fue exitosa o fallida.
req.always(ocultarAjaxLoaderIcon);
console.log('esto se ejecuta primero que nada, y luego las acciones definidas para req');
Ahora imaginemos que queremos que cierta funcionalidad se ejecute cuando ya se obtuvo la respuesta para dos requests distintas?? Fácil, usamos $.when
//armarPagina se ejecuta solo si se obtienen las respuestas exitosas
// a las dos requests.
$.when( $.get('post.php'), $.get('comments.php') ).done(armarPagina);
function armarPagina(a, b){
// a es un array con los argumentos que recibiria de la primer request,
// b lo mismo pero para la segunda request.
console.log(a[2].responseText);
console.log(b[2].responseText);
}
¿Esto sólo?
No – Como fué antes mencionado el uso de las Deferreds no se limita a AJAX, sino que es una idea más arquitectural.
Uno puede crear sus propias deferreds usando $.Deferred(), pero eso lo vamos a ahondar más en detalle en un futuro post. Si no te aguantás, podés echarle un vistazo a Deferred Object en la documentación oficial.