[Win8 JavaScript] Patrón Promise

Una de las novedades que nos encontraremos a la hora de afrontar una aplicación Windows Store app , es que la mayoría de veces que interactuemos con sus APIS tendremos que hacerlo de forma asíncrona. De esta manera nuestra aplicación no afectará de forma directa al sistema y la respuesta ante el usuario será más ágil y rápida.

Un ejemplo muy gráfico sería acceder a un fichero del sistema:

Windows.Storage.FileIO.readTextAsync(fichero).then(function (contenido) {

//Mostrar contenido

  });

En este ejemplo hemos utilizado la API WinRT para poder acceder al contenido de un fichero de forma asíncrona.  En cuanto se accede al contenido del fichero se lanza la función anónima que está dentro del “then”  mientras la ejecución de nuestra aplicación ha continuado. De esta manera solo mostraremos el contenido del fichero si hemos podido acceder al mismo sin bloquear la aplicación.

Por convención los nombres de las funciones asincrónicas terminan en “Async“. De esta manera puedes saber que la ejecución se producirá después que se devuelva la llamada.

Habitualmente se utilizan llamadas asíncronas para los procesos como:

  • Mostrar un cuadro de diálogo de mensaje
  • Trabajar con el sistema de archivos
  • Enviar datos a Internet y recibirlos

¿Cómo funcionan las llamadas asíncronas en las Windows Store app?

Para trabajar de forma asíncrona tanto WinRT com WinJS utilizan el patrón Promise. Este patrón se puede utilizar para gestionar las llamadas asíncronas de una manera facil de seguir y encadenar.

Un objeto Promise devolverá un resultado en algún momento en el tiempo, por ejemplo la función then nos permitirá actuar cuando la promesa se cumpla y para eso disponemos de tres parámetros.

promise.then( onComplete, onError, onProgress);
  • onComplete : función que se lanza cuando se cumple la promesa.
  • onError :  función que se lanza cuando hay un error.
  • onProgress : función que se lanza cuando se notifica un cambio en el progreso de la promesa.

Siguiendo con el ejemplo anterior veremos como capturar los errores y las notificaciones de progreso.

Windows.Storage.FileIO.readTextAsync(fichero).then(
      function complete(res) {
            document.getElementById("result").textContent = res;
      },
      function error(res) {
            document.getElementById("error").textContent = "Error";
        },
      function progress(res) {
            document.getElementById("progress").textContent = "Progress";
 });

Otra función que podemos utilizar es done.

promise.done(onComplete, onError, onProgress);

La diferencia es que en el caso de un error en el procesamiento, la función then devuelve un objeto promise en el estado de error pero no inicia una excepción. Mientras que el método done inicia una excepción si no se proporciona una función de error.

Además then devuelve una promesa lo que nos permite el encadenamiento de promesas, mientras que done devuelve undefined. Por eso se recomienda usar then para una etapa intermedia de la operación (por ejemplo .then().then()) y done para la etapa final de la operación (por ejemplo, .then().then().done()).

Encadenamiento de Promise

Como then devuelve una promesa, puedes encadenar mas de una función asíncrona para su ejecución.

  var divResultado = document.getElementById("result");

  WinJS.xhr({ url: "http://localhost:32999/api/values" })
           .then(function (result) {
               divResultado.innerText = result.responseText;
            return result;
           })
           .then(function (result) {
               divResultado.style.backgroundColor = "green";
           },
             function (error) {
                 divResultado.style.backgroundColor = "red";
                 divResultado.innerText = "Error";
  });

En este ejemplo se puede comprobar como realizar una llamada asíncrona a un servicio de datos externo, en el primer then trataremos los datos, el segundo then destacaremos en la interfaz que los datos se han cargado correctamente y luego tendremos una función  para tratar las excepciones.

Es recomendable encadenar las promesas y no anidarlas para una mejor lectura del código y un mejor seguimiento de los errores.

Si lo preferimos podemos tener una función global para el tratamiento de los errores.

WinJS.xhr({ url: "http://localhost:32999/api/values" })
           .done(function (result) {
               divResultado.innerText = result.responseText;
 });

 WinJS.Promise.onerror = function errorhandler(event) {
         var ex = event.detail.exception;
         var promise = event.detail.promise;
   };

Pero si utilizamos el evento onerror para capturar el error en tiempo de ejecución, estaremos limitando un poco el control de los errores de las llamadas asíncronas.

 

Por último hay que tener en cuenta que para procesos muy largos la mejor opción es utilizar tareas en segundo plano.

Para más información visitar la página oficial de msdn donde hay gran cantidad de información y ejemplos sobre el desarrollo de aplicaciones Windows Store App

cross-posting: http://mrubino.net

 

Update: 22/10/2012
Por si no queda muy claro, Promise no hace que nuestro código sea asíncrono. Promise es una implementación del Patrón Observable y nos ayuda a gestionar nuestras llamadas asíncronas de una manera más sencilla. Las funciones asíncronas son las própias de WinRT acabadas en “Async“.

 

 

[Win8] JavaScript – Llamadas a servicio de datos externos “Cross-Domain”

Con la llegada del nuevo sistema operativo de Microsoft, se nos abre un inmenso abanico de posibilidades a los desarrolladores web que no podemos desaprovechar. Ya que podemos crear estas nuevas aplicaciones con HTML 5 y JavaScript para que corran de forma nativa en la nueva interfaz. De esta manera podremos reutilizar todos nuestros conocimientos y nuestra experiencia en realizar aplicaciones que podrán ser consumidas directamente por millones de usuarios potenciales gracias a la nueva tienda de Windows.

Lo primero que tenemos que tener claro es que las aplicaciones web pueden ejecutarse en dos contextos diferentes.

  1. El usuario puede abrir la aplicación web desde el navegador que más le guste como se ha realizado siempre. Y la aplicación se ejecutara en un entorno limitado donde no se podrá acceder a las nuevas Apis que ofrece el sistema “WinRT, WinJS”.

  2. El usuario ejecutara una aplicación de la App Store realizada con HTML y javascript, en este caso la aplicación se ejecutará de forma nativa y en un contexto local. Tenemos acceso a las Apis del sistema para disfrutar de todas las ventajas que nos ofrece Windows 8 accesible directamente desde nuestro código JavaScript.

Si ya tenemos claro estos dos escenarios, tendremos que adaptar un poco nuestras aplicaciones web para que funcionen correctamente como aplicación nativa en Windows8.

Por ejemplo si queremos consumir un servicio externo de datos “cross-domain” sin problemas, podemos utilizar la librería WinJS para hacer una llamada XmlHttpRequest de forma asíncrona y recuperar estos datos de una forma muy sencilla.

 WinJS.xhr({ url: "http://localhost:32999/api/values" }).then(
               function (response) {
                   var json = JSON.parse(response.responseText);
                   json.forEach(function (item) {
                       items.push(item);
            }
        );
 } );

Este ejemplo accede a un servicio WebApi que retorna una lista de datos en formato JSon, hay que destacar que está utilizando el patrón Promise para realizar la llamada de forma asíncrona como casi siempre que interactuemos con las Apis de Windows 8.

Los parámetros que acepta esta función son:

  • type: (opcional). Especifica el Verbo HTTP a utilizar GET, POST, HEAD “petición sin cuerpo de mensaje”. Por defecto la llamada es GET.
  • url: URL del servicio de Datos.
  • user: (opcional). String con el nombre de usuario para la autentificación.
  • password: (opcional). String con la contraseña para la autentificación.
  • headers: (opcional). Objeto cuyas propiedades se usan como nombres de encabezado y los valores de las propiedades se utilizan como valores de estas propiedades del encabezado.
  • data: (opcional). El objeto que se pasa como datos en la llamada.
  • customRequestInitializer: (opcional). Función para utilizar antes de lanzar la llamada XmlHttpRequest.

Un ejemplo de una llamada estableciendo un parámetro en la cabecera, para asegurarse que la respuesta no está cacheada.

WinJS.xhr({
    url: " http://localhost:1489/api/demo ",
    headers: {
        "If-Modified-Since": "Mon, 27 Mar 1972 00:00:00 GMT"
    }
})
    .done(function complete(result) {
        ...
    });

Para más información sobre las propiedades que acepta la cabecera visitar HTTP Response Headers:

Y para finalizar veremos un ejemplo completo de una llamada a un servicio de datos enlazado a un ListView:

  1.  
    1. El origen de los datos que consulta al servicio: Un fichero Datos.js donde se especifica un espacio de nombres “Data” que utilizaremos más adelante para enlazar los datos con el control.

(function () {
    var items = new WinJS.Binding.List();

    WinJS.xhr({ url: "http://localhost:1489/api/demo" }).then(
               function (response) {
                   var json = JSON.parse(response.responseText);
                   json.forEach(function (item) {
                       items.push(item);
                   }
               );
               }
             );

    var publicMembers = { Modelos: items };

    WinJS.Namespace.define("Data", publicMembers);
})();

     2. Enlazamos los datos al control de lista de forma declarativa:

  <div id="lista"  data-win-control="WinJS.UI.ListView"  
             data-win-options="  {
                itemDataSource : Data.Modelos.dataSource,
                layout:{type: WinJS.UI.GridLayout}
         }" >
   </div>

xi1bz

Con este sencillo ejemplo podemos comprobar que sencillo es recuperar los datos de un origen externo sin tenernos que preocupar por el cross-posting ya que se ejecuta la llamada en el contexto local y además se enlaza los datos de forma declarativa con nuestro control HTML.

 

Cross-Posting: http://mrubino.net