Para el servidor, una petición es indistinta de otra inmediata y el ciclo comienza nuevamente. Es por esto que se dice que el protocolo HTTP no tiene estado, o sea, es stateless. Es nuestra responsabilidad como desarrolladores solventar esta limitación y asegurarnos de mantener el estado entre peticiones.

Para esta tarea, era muy habitual utilizar cookies, estos pequeños pedacitos de información que viajan en cada petición entre el cliente y el servidor, de manera de recordarle “Hey! te acuerdas de mi?”. Entonces, si tenemos el problema resuelto, ¿para qué necesitamos otro método nuevo? Bueno, resulta que las cookies no son tan buena solución:

  • Aumentan el peso de cada petición al servidor, ya que toda la información guardada en las cookies debe viajar al servidor y volver.
  • Tienen una limitación de 4kb de espacio disponible.
  • No todo el mundo tiene las cookies habilitadas, sobre todo luego de que se publicaran como ciertas empresas utilizan las cookies para registrar nuestro comportamiento en la web.

Por suerte, una de las nuevas específicaciones de la W3C y WHATWG, indica como podemos utilizar Javascript para guardar información en los navegadores de nuestros usuarios que expire al finalizar la sesión, o que no expire en lo absoluto a menos que el usuario lo indique.

Entran a la cancha Local Storage y Session Storage

¿Qué son Local Storage y Session Storage?

Puesto de manera sencilla, son dos métodos que nos ofrecen los navegadores para guardar pares de valores llave/valor, de manera sencilla y dándonos de manera standard unos 5 megabytes. Al igual que las cookies, ésta información no desaparece si uno navega fuera de la página.

Al contrario de las cookies, esta información queda guardada en la computadora del cliente y no viaja con cada petición. No hay excusas para no utilizar ninguno de los dos métodos, ya que se encuentran soportados por prácticamente todos los navegadores, excepto IE6, IE7 y Safari 3.2. Comenzar a utilizarlo es muy sencillo:

 /* Utilizando la Api de Local Storage, podemos guardar y luego recuperar un valor */

localStorage.setItem('saludo', 'Bienvenidos a localStorage');
valor = localStorage.getItem('saludo');

/* Pero esta no es la única manera, ya que también podemos hacer lo siguiente */

localStorage['saludo'] = 'Este saludo también es válido';
valor = localStorage['saludo'];

/* Y claro, esto también es válido */

localStorage.saludo = 'Y este es mi método favorito';
valor = localStorage.saludo;

Pero esto no es lo único que podemos hacer, ya que las APIs de Local Storage como la de Session Storage nos ofrecen un par de métodos más para controlar la información que contienen.

 /* Podemos saber cuántos elementos tenemos guardados */

alert('Tenemos ' + localStorage.length + ' elementos dentro de Local Storage');

/* Eliminar elementos también es muy sencillo de lograr */

localStorage.removeItem('saludo');

/* Y si queremos acceder a la información de localStorage de manera secuencial,
   entonces lo hacemos gracias a su método 'key' que devuelve la clave en
   determinada posición */

    for(var i=0, t=localStorage.length; i < t; i++) {
    key = localStorage.key(i);
    alert('Para la clave ' + key + ' el valor es: ' + localStorage[key]);
}

Vale aclarar que todo lo mismo vale en este punto, si queremos trabajar con sessionStorage, que guardará los valores durante el tiempo de sesión del navegador y se borrarán cuando el navegador se reinicie. Esto no sucede con localStorage, que conservará los valores aunque cerremos y volvamos a acceder al navegador.

¿Cómo cargar local Storage de manera más inteligente?

Luego de estar trabajando un tiempo con localStorage o sessionStorage, comienza a notarse un patrón cuando queremos asegurarnos de la disponibilidad del Storage. Al comienzo hacemos las siguientes pruebas:

if('localStorage' in window && window['localStorage'] !== null) {
    alert('Genial, tenemos un navegador decente que soporta LocalStorage');
    var storage = localStorage
} else { alert('Como seguimos utilizando un navegador viejo, Santa Claus no nos traerá nada esta Navidad'); }

Como puden ver, el utilizar el if para chequear la existencia de localStorage se puede volver un tanto molesto y un “anti-patrón”. ¿Y qué pasa si no tenemos soporte para localStorage? ¿Nuestra aplicación debe fallar? ¿O deberíamos ofrecer una solución que degrade de manera “grácil”?

Aquí es donde surge la necesidad de trabajar con un método que sirva ya en tanto tengamos soporte, o no. Y que no de errores al momento de chequear la disponibilidad de localStorage. Este es el código que estoy utilizando en mis desarrollos:

// Existe localStorage?
var storage;
try {
    if (localStorage.getItem) {
        storage = localStorage;
    }
} catch(e) {
    storage = {};
}

De esta manera, dentro de la variable storage tendremos o una referencia a localStorage lista para utilizar, o un objeto que no reemplazará la funcionalidad de localStorage, pero al menos no dará error inmediatamente cuando lo utilicemos, para luego implementar alguna librería que de soporte, como Amplify.js.

No Strings Attached

Una de las limitantes más molestas de localStorage y sessionStorage es que, al igual que con las cookies, solo podemos guardar cadenas de texto. Nada de valores nativos como enteros, float, ni siquiera un triste boolean (que sería “false”). Ni hablar de los objetos:

 persona = {nombre: 'Pablo Rigazzi', edad: 33};
localStorage.autor = persona;
alert(localStorage.autor);

// Esto nos devuelve el texto
"[object Object]"

Entonces, ¿cómo podemos hacer para superar esta limitante? Afortunadamente, hay una técnica bastante simple para guardar objetos completos dentro de localStorage, para luego recuperarlos y volver a convertirlos y este método es utilizar el objeto JSON, disponible en los mismos navegadores que soportan localStorage. Retomando el ejemplo anterior:

 // Primero convertimos el objeto en una cadena de texto

localStorage.autor = JSON.stringify(persona);

/* Y ahora, al recuperarlo, convertimos el string nuevamente en un objeto */

var autor = JSON.parse(localStorage.autor);
alert(typeof autor);

// Aqui obtendremos que el tipo de la variable es "object"

Vientos de cambio

Los navegadores nos dan un mecanismo para enterarnos cuando otra página realizó cambios sobre los valores de localStorage (y solo localStorage). Para esto, lanzan un evento llamado “storage” que es muy fácilmente atrapable. Lo malo, no es un evento “cancelable”, esto eso, el navegador solamente nos avisa del cambio, pero no tenemos manera de impedir que ese cambio se realice.

Hay que tener en cuenta que este evento se dispara si el cambio se realizó en otra página (abierta en otra ventana o en otro tab), que esté asociada al mismo localStorage con el que estamos trabajando (o sea, es del mismo dominio).

 hay_cambio = function(e) {
    console.log("Dentro de la clave " + e.key + " el valor anterior era " + e.oldValue + ' y el nuevo es ' + e.newValue);
}
if(window.addEventListener) {
    window.addEventListener('storage', hay_cambio, false);
} else {
    // Hay que soportar IE6, 7 y 8, lo lamento
    window.attachEvent('onstorage', hay_cambio);
}

Aquí es donde para probar, podemos abrir otra ventana del mismo dominio donde estemos trabajando, disparar FireBug o la consola de Chrome y tipear: localStorage.clave = "nuevo valor." y ver como se dispara el evento en el tab o ventana original.

Ejemplos de utilización de Session Storage y Local Storage:

  • Guardar resultados temporalmente de una llamada a un webservice.
  • Mantener estado de la interfaz de usuario de manera sencilla.

Referencias