Entre las mejores prácticas para mejora de rendimiento de tu sitio web, hay una que siempre había pasado por alto y sin prestar mucha atención: cachear las peticiones AJAX. Esto significa que es recomendable conservar todas las consultas que vayamos haciendo a nuestro servidor, de manera que si ya hemos solicitado la información una vez, la reutilicemos nuevamente sin perder el tiempo en una nueva petición.
Por ejemplo. Imaginaos que utilizamos una aplicación con un funcionamiento parecido al de Gmail. Es decir, nos basamos en una única página que va modificando su contenido “central”, pero las cabeceras, barras laterales… son siempre las mismas. Si las peticiones no estuviesen cacheadas, tendríamos que hacer la consulta cada vez que un usuario cambiase de vista (de bandeja de entrada a bandeja de salida, por ejemplo). ¿Otra consulta?. ¡Si ya la he hecho!.
He estado haciendo unas pequeñas pruebas de funcionamiento y rendimiento, y me gustaría compartirlas con vosotros.
Ejemplo inicial
En este ejemplo, tendremos un enlace donde cada vez que hagamos click, haremos una consulta a Twitter. Esta consulta nos devolverá una búsqueda en formato JSON de ciertos tweets. A lo largo del ejemplo, utilizaremos jQuery y la función $.ajax(). Fácil.
En el código inicial, he añadido un medidor de tiempo para que podamos ver cuánto tarda en ejecutarse totalmente la petición.
$(”#enlace”).click(function(){console.time( “Peticion AJAX” ); $.ajax({ url: “http://search.twitter.com/search.json?q=genbetadev&count=5&callback=?”, type: “GET”, dataType: “json”, success: function( resp ) { console.log( resp ); }, complete: function( ){ console.timeEnd( “Peticion AJAX” ); } });
});
Después de hacer click varias veces, los resultados que aparecían en la consola de Firebug eran:
Bien, yo estoy en mi contenido principal y hago una consulta de tweets. Navego hacia otro sitio. Navego de vuelta al contenido anterior y… ¿vuelves a hacerme la consulta?. No parece tener mucho sentido.
En este punto hay que aclarar, para los más puristas, que por ser una aplicación asíncrona, no significa que sea instantánea. Información como ésta (una búsqueda de tweets, emails recibidos, etc.) es posible que no cambie durante muchos minutos. Por lo tanto, no es necesario una inmediatez de los resultados.
Yo lo que quiero es que vaya rápido, que para eso ya lo he buscado antes.
Cache de jQuery
Lo primero que intenté hacer fue utilizar una opción de jQuery en $.ajax(), fijando la opción de cache a true.
$(”#enlace”).click(function(){console.time( “Peticion AJAX” ); $.ajax({ url: “http://search.twitter.com/search.json?q=genbetadev&count=5&callback=?”, cache: true type: “GET”, dataType: “json”, success: function( resp ) { console.log( resp ); }, complete: function( ){ console.timeEnd( “Peticion AJAX” ); } });
});
Haciendo lo mismo que antes (varios clicks), los resultados obtenidos mostraban unos datos curiosos. Los tiempos de ejecución eran exactamente iguales a los anteriores. Es decir, el caché no hacia nada. Esto me llevo a pensar que: o no funciona correctamente o no lo estaba haciendo bien… o los dos.
En cualquier caso, yo quería algo más agresivo, así que seguí pensando un poco más.
Cache Megaduradero
En este momento me acordé de la función data() de jQuery. Podía realizar una búsqueda, guardarla con la función data() y después volver a utilizarlo. Premio.
var doc = $(document);
$(”#enlace”).click(function(){console.time(“Peticion AJAX”); var cache = doc.data( “cache” ); if( cache ) { console.log( cache ); console.timeEnd(“Peticion AJAX”); return false; } $.ajax({ url: “http://search.twitter.com/search.json?q=genbetadev&count=5&callback=?”, type: “GET”, dataType: “json”, success: function( resp ) { doc.data(“cache”, resp); console.log( resp ); }, complete: function( ){ console.timeEnd(“Peticion AJAX”); } }); return false;
});
El funcionamiento es muy sencillo. La primera vez que se ejecuta el código, buscamos por la variable “cache”. Si está vacía, hace la consulta AJAX. Una vez hecha la consulta, guarda el valor y termina. Si volvemos a repetirlo, encontrará el valor “cache” y lo utilizará.
Nuevamente, en la página hice click varias veces para ver los resultados que mostraba Firebug.
Wow, ¡esto es otra cosa!. Hemos pasado de 665ms a 8ms. Es decir, unas 80 veces menos tiempo de respuesta.
Por desgracia, el problema se ve a primera vista. Una cosa es que los datos sean cacheados y otra distinta es que los resultados serán siempre los mismos. Si estamos media hora en la misma página, ya pueden aparecer 300 tweets nuevos que nosotros no los veremos: utilizaremos los que había hace media hora.
Así que seguí pensando y se me ocurrió otra solución.
Cache Semiduradero
La solución pasa por fijar un tiempo límite (10 minutos, por ejemplo) y, si hemos pasado ese tiempo, actualizar los datos que teníamos previamente. De esta manera, garantizamos que los datos estarán actualizados y, además, la mayoría de ellos habrán sido obtenidos desde el cache.
Así que, alargando un poco más la función:
var doc = $(document), segundos = 10;
$(”#enlace”).click(function(){console.time(“Peticion AJAX”); var cache = doc.data( “cache” ); if( cache ) { if( (new Date().getTime() – (segundos * 1000)) < cache.time ) { console.log( cache.data ); console.timeEnd(“Peticion AJAX”); return false; } } $.ajax({ url: “http://search.twitter.com/search.json?q=genbetadev&count=5&callback=?”, type: “GET”, dataType: “json”, success: function( resp ) { doc.data(“cache”, { “time” : new Date().getTime(), “data” : resp }); console.log( resp ); }, complete: function( ){ console.timeEnd(“Peticion AJAX”); } }); return false;
});
Como ves, el aspecto es muy parecido al ejemplo anterior. Se ha fijado un tiempo máximo de 10 segundos (en una aplicación real, establecería este valor según lo que crea conveniente: 5 minutos, 10 minutos, etc.). Superado ese tiempo, se volverá a actualizar la consulta anterior con datos nuevos.
Mucho mejor.
Conclusiones
En ningún momento esta entrada tendría que tomarse como una guía a cumplir. Solo han sido ciertos experimentos que he ido haciendo y me han parecido curiosos. Como ya se habló en Genbeta, la importancia de la optimización de webs es cada día más importante y esta es un aspecto en el que me parece interesante trabajar.
Por supuesto, si alguien tiene cualquier otra idea con la que poder seguir mejorando estos ejemplos, me encantaría leerlas y ver si, entre todos, conseguimos ir un poquito más allá.