El Patrón Promise

El patrón Promise es precisamente nuevo, pero está creciendo en popularidad a raíz del gran uso que se hace de él en la API de WinJS, el wrapper JavaScript para hacer a la nueva API WinRT en Windows 8. El objetivo de este patrón es facilitar el modelado de procesos asíncronos, de forma que el código que consuma operaciones que no van a retornar inmediatamente sea lo más legible y fácil de escribir posible.

Escenario actual

La asincronía está de moda. La Nube, aplicaciones “responsivas”, etc. Todo parece empujar a los desarrolladores a hacer un uso cada vez mayor de este tipo de construcciones programáticas. Microsoft ha decidido hacer una apuesta decidida por la asincronía en las aplicaciones Metro con Javascript, por muchas razones.

En primer lugar hay que tomar en consideración también el hecho de que JavaScript sea un lenguaje de ejecución basado en un único hilo, lo que hace que cualquier tarea de larga duración congele la interfaz gráfica, con la consiguiente mala imagen para la aplicación frente al usuario. Además, la visión de Microsoft con respecto a las aplicaciones Metro se basa en fluidez, rapidez de respuesta y sensación de inmediatez, de ahí que la asincronía pase de ser una característica deseada a una obligación absoluta.

Por estas razones Microsoft ha decidido ofrecer versiones asíncronas de la gran mayoría de llamadas al sistema que WinRT pone a disposición de los desarrolladores. Estas llamadas van a implementar, en su gran mayoría, el patrón Promise, para simplificar su uso asíncrono.

Pero, ¿cómo es este patrón? ¿Qué características tiene y cómo podemos usarlo? Veámoslo.

El Patrón

Promises/A es el nombre del estándar detrás de este patrón. En él se propone que los objetos Promise tengan una propiedad then que será una función con tres parámetros:

  • Handler para gestionar el resultado satisfactorio.
  • Handler para gestionar los errores.
  • Handler para gestionar el progreso de la operación.

Al retornar un objeto promise la función estará “prometiendo” retornar un valor en algún punto del futuro próximo.

Esta implementación del estándar es exactamente la misma que ha hecho Microsoft en su librería. En el siguiente fragmento de código

myWebService.get("http://www.javierholguera.com")
    .then(
       function(result) { /* gestionar resultado correcto */},
        function(error) { /* manejar error */},
       function(progress) { /* informar sobre avances */}
     );

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Para terminar uno de los principales beneficios del patrón es la facilidad para componer unos promises con otros. Por ejemplo, imaginemos que queremos realizar un proceso asíncrono A y cuando hayamos terminado dicho proceso satisfactoriamente, quedemos ejecutar un segundo proceso asíncrono B. Con el siguiente fragmento de código podemos ver cómo lo haríamos.

procesoA.ejecutar("http://www.javierholguera.com")
    .then(function(result) {
         // gestionamos el resultado de A ejecutando el proceso B
        // esta funcion retorna el promise del proceso B
        return procesoB.ejecutar(result);
    })
    .then(function() {
        // gestion del exito del proceso B
        console.log('B finalizo correctamente');
    }, function(error) {
        // gestion del error del proceso B 
        console.log('B finalizo con errores');
    });

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Podemos apreciar en el código como el segundo .then está ejecutándose sobre el resultado retornado por el primer .then, que es en realidad el promise correspondiente al proceso B. En este caso he omitido, por claridad, el handler para el error en el promise del proceso A, pero podría haberse añadido perfectamente justo a continuación del manejador para el resultado exitoso del proceso A.

Conclusión

Promise es un patrón sencillo pero que nos ayuda a gestionar la asincronía de una forma fácil y limpia. Este patrón tiene una larga vida por delante, como parte fundamental de la API de WinJS en Windows 8, por lo que cuanto antes lo dominemos, antes empezaremos a escribir co´digo asíncrono como las aplicaciones Metro nos exigen.