El JavaScript con el que nos vamos a encontrar en el desarrollo de aplicaciones Metro es 100% estándar. En concreto, es una implementación del estándar ECMAScript5, la versión más reciente del lenguaje aparecida en 2009 y en cuya elaboración Microsoft tuvo una participación muy activa, entre otras cosas aportando una completa suite de pruebas.
En ECMAScript5 aparecieron una gran cantidad de novedades, como propiedades, métodos para parsear JSON, nuevas funciones en los prototipos de Array, string, etc. Todas ellas estarán disponibles para su uso en Aplicaciones Metro, incluida la característica estrella de ECMAScript5: el Modo Estricto.
Strict Mode
El Modo Estricto es probablemente la característica más importante aparecida con la última versión de JavaScript. Este modo configura una versión más estricta de JavaScript, que además cambia algunas de las semánticas del lenguaje. El resultado es un código de más calidad, más seguro y más eficiente, aunque menos permisivo con ciertas prácticas desaconsejadas. Los cambios de Strict Mode se pueden agrupar en:
Eliminación de ciertas relajaciones de JS que no producen en errores, pero que se consideran malas prácticas. En Strict Mode sí van a producir errores.
Eliminación de ciertos fallos de JS que impiden a los compiladores optimizar el código tanto como sería posible.
Imposibilidad de utilizar ciertas palabras que van a ser reservadas en la próxima versión de JS.
La activación del modo estricto se puede realizar tanto a nivel de script como de función, siendo la segunda la forma recomendada. En los scripts que aparecen en todas las plantillas de proyectos que trae consigo Visual Studio 2012 este modo viene activado por defecto.
No visible por defecto
JavaScript es un lenguaje que siempre se ha caracterizado por dar lugar a un código en el que existía una excesiva exposición de los detalles internos de unas partes del código hacia a otras, entre otras razones por la facilidad con la que se podía definir elementos en el espacio de nombres global del programa.
Sin embargo en las Aplicaciones Metro estas prácticas que harían revolverse en su tumba a David Parnas si no fuera porque sigue vivo, van a terminarse, al menos si seguimos las recomendaciones de Microsoft al respecto.
Lo que podemos ver en el listado anterior es el aspecto habitual de un fichero JavaScript en Aplicaciones Metro. En él se conjugan el punto anterior y el actual:
La instrucción use strict indica que esta función se va a ejecutar en modo estricto.
Todo el código va a estar definido dentro de una función anónima. Esta buena práctica ayuda a que no se contamine el espacio de nombres global, al tiempo que limita el alcance de lo definido dentro de la función anónima a sí misma.
Por supuesto, hacer uso de esta técnica no sólo tiene ventajas; también existen algunos inconvenientes, lógicos por otra parte. Al no existir visibilidad desde fuera de la función anónima para todo lo que se haya definido en su interior, otras partes del código no podrán llamar a ninguna función o acceder a ninguna variable que se defina dentro de la función anónima.
Esto permite evitar muchos errores indeseados, pero también dificulta ligeramente escenarios habituales como definir un manejador para un evento y asociarlo en el HTML de la página. Si recurrimos a este mecanismo el evento del control HTML no será capaz de llamar a su manejador por no tener visibilidad sobre el mismo. Sin embargo, resolverlo es tan sencillo como utilizar el método addEventListener para registrar, para el control y el evento, el manejador correspondiente.
Por otra parte, no sólo vamos a necesitar hacer visibles nuestros manejadores de eventos. Hay otras partes del código JavaScript, como variables u otro tipo de funciones que no sean manejadores, que son susceptibles de ser accedidos o ejecutados desde otras partes de la aplicación. Para resolver este problema tendremos a nuestra disposición la función WinJS.Namespace.define.
Esta función acepta un nombre para el espacio de nombres que va a crear y un objeto que representa los miembros que se van a añadir al mismo. En el siguiente fragmento de código podemos ver un ejemplo de su uso extraído del fichero data.sql que crean automáticamente algunas de las plantillas de proyecto de Visual Studio 2012.
El resultado será la creación de un espacio de nombres Data con los elementos items, groups, getItemsFromGroup, getItemReference, resolveGroupReference y resolveItemReference. Estos elementos pueden ser variables o funciones, indistintamente.
En resumen, en las Aplicaciones Metro se recomienda que las funciones y variables no sean visibles por defecto y que, a posteriori y una vez que lo tengamos claro, las hagamos visibles explícitamente, para que puedan ser accesibles desde fuera de las funciones anónimas que van a contenerlas.
Programación asíncrona
Una de las características básicas de una Aplicación Metro es su rapidez, un requisito indispensable a la hora de usar interfaces táctiles, puesto que el tiempo de reacción esperado por el usuario se reduce enormemente en comparación con interfaces basadas en ratón y teclado.
Como veíamos antes WinRT es una API fuertemente orientada a la programación asíncrona, puesto que cualquier llamada que pueda demorarse más de 50 milisegundos va a hacerse asíncronamente.
Además, dado que WinRT no es más que una proyección de una misma API a todos los lenguajes que se pueden utilizar con ella, en todos Microsoft se ha encargado de introducir mecanismos para simplificar enormemente el código asíncrono, como es el caso de C# y C++ con las nuevas instrucciones async/await.
Sin embargo, la implementación de JavaScript que se utiliza en Aplicaciones Metro es 100% una implementación del estándar ECMAScript5, por lo que en este caso no había la posibilidad de modificar el lenguaje. En su lugar se ha decidido apoyarse en el patrón Promise y promocionar el uso de WebWorkers con una completa implementación de su API.
Patrón Promise
Este patrón es la base principal de la programación asíncrona en WinRT. Se basa en el objeto Promise, que devolverá un valor en algún momento posterior. Gracias a estos objetos será posible construir cadenas de llamadas asíncronas con un código realmente limpio y fácil de leer.
La base de Promise es su propiedad then, que devuelve una función que puede recibir hasta tres parámetros de tipo función:
El primer parámetro será la función a ejecutar si el resultado del método fue exitoso.
El segundo parámetro será la función a invocar si el método falló.
El tercer y último parámetro será la función que el método podrá ir invocando a medida que vaya realizando progresos en su ejecución.
El aspecto que tiene una llamada a una función que devuelve un objeto Promise como resultado inicial, además de una implementación para sus tres parámetros, podemos verla en el siguiente fragmento.
Por último, encadenar una sucesión de llamadas asíncronas resulta muy sencillo, al tiempo que no se pierde legibilidad en el código resultante.
Estándar Web Workers
Otro mecanismo al que podemos recurrir para crear Aplicaciones Metro fluidas y veloces es el nuevo estándar Web Workers. Esta nueva API nos va a permitir ejecutar un script en background, de forma que aquellas tareas que sean más intensivas y puedan afectar al rendimiento del hilo de ejecución principal, puedan descargarse a Web Workers.
Cada Web Worker va a tener la posibilidad de comunicarse con el hilo principal mediante un sistema de mensajería que le permitirá enviar y recibir mensajes.
Es importante tener en cuenta que los Web Workers no son los threads de JavaScript, puesto que cada worker va a tener acceso a una copia de información limitada, menor que el hilo principal. En el siguiente gráfico extraído de MSDN podemos ver las diferencias entre la información del hilo principal y la que es visible para cada web worker.
Aunque estas restricciones pueden resultar incómodas para programadores acostumbrados a un entorno multihilo completo (como el que ofrecen la mayoría de lenguajes nativos), la ventaja de este enfoque es que no tendremos que preocuparnos de elementos típicos de la programación multihilo, como condiciones de carrera, interbloqueos, etc.
Rendimiento en JavaScript
Como hemos visto en los puntos anteriores, Microsoft está muy concienciada con la necesidad de que las Aplicaciones Metro ofrezcan un rendimiento sobresaliente. Además del amplio soporte y, a efectos prácticos, la obligación de realizar programación asíncrona para asegurar que las aplicaciones son lo más fluidas posible, también se han implementado otros mecanismos para asegurar un resultado óptimo.
Estos mecanismos son numerosos, así como las recomendaciones y buenas prácticas que Microsoft está empujando para que utilicemos en el desarrollo de Aplicaciones Metro, demasiados como para describirlos en un solo artículo. Tomaremos como ejemplo uno de los aspectos en los que más foco se está haciendo: el arranque de la aplicación.
Uno de los requisitos que toda Aplicación Metro debe cumplir es su activación en un tiempo menor de 15 segundos. Ese será el tiempo que Windows 8 desde que una aplicación muestra su Splash Screen hasta su carga inicial debe haber terminado. Aunque existen mecanismos para extenderlo, ofreciendo una pantalla interactiva que ayude al usuario a seguir esperando evitando la sensación de que la aplicación se ha colgado, en general debemos intentar mantenernos dentro de esos 15 segundos.
En JavaScript existen varias recomendaciones para mejorar los tiempos de carga:
Incluir en el paquete de la aplicación todos los ficheros de scripts, CSS e imágenes que sea posible, puesto que un acceso a disco es mucho más veloz que a través de red.
Cargar estáticamente los scripts principales enlazándolos en la página inicial de la aplicación.
Cargar diferidamente los demás scripts.
Sacarle partido al cacheo de bytecodes.
Bytecode caching es una técnica que emplea Windows 8 con los ficheros JavaScript. Cuando creamos un paquete de Aplicación Metro con HTML/JS, en su interior, además de recursos como páginas HTML o imágenes, tendremos ficheros de JavaScript. Durante su instalación Windows 8 convertirá estos ficheros JavaScript a sus equivalentes in bytecode.
Estos bytecodes van a cachearse y no será necesario volver a generarlos en las sucesivas activaciones de las aplicaciones, a no ser que instalemos una nueva versión de la aplicación. Según Microsoft los bytecodes mejoran hasta un 30% los tiempos de carga de las aplicaciones.
Por último, para beneficiarse de este sencillo pero potente mecanismo, es necesario que se cumplan dos requisitos: los ficheros JS deben estar codificados en UTF-8 con una marca BOM y deben estar enlazados estáticamente en la página HTML de inicio de la aplicación.
En Genbeta Dev | Programar aplicaciones Metro de Windows 8
Es especialista en metodologías ágiles y en estándares web relacionado con HTML5. Es miembro activo de la comunidad .NET a través de MADNUG y colabora con medios como DotNetMania, PC Actual o Desarrolloweb.com.
Puedes seguirle en Twitter: @javierholguera o en su blog: javierholguera.com