Cuando queremos brindarle al usuario una experiencia rápida y fluida, lo más importante es no dejar que la interfaz se congele mientras se ejecuta alguna operación adicional, tal como cargar un archivo o conectarse a un servicio.

En la antigüedad, esto se lograda con Threading, luego BackgroundWorkers y después TPL. Pero la evolución continuó para bien y hoy se logra a través de métodos asíncronos.

Es un mecanismo por defecto del Framework 4.5 mediante el cual un método marcado como asíncrono, no congela la interfaz de usuario, pues por debajo empieza a correr en otro hilo distinto a aquel que hizo el llamado.

Para marcar un método como asíncrono, le ponemos la palabra clave async antes del tipo de retorno del mismo.

private async void ProcedimientoAsincrono()
{
   for (int i = 0; i < 5; i++)
   {
       //Ejecutar subproceso i
} }

En este caso, el proceso  ejecuta todos los subprocesos, sin que la interfaz se congele, dado que ese es uno de los principales motivos de disgusto de los usuarios con las apps.

Ilustración de un circuito contador asíncrono

 

Sin embargo, dado que se inicia otro hilo de la app para ejecutar otro proceso… cuándo sabemos que este acaba? Cómo hacemos para proceder en consecuencia?

Por ejemplo: Si queremos cargar tres páginas de contenido bajado de la web, necesitamos saber cuándo tenemos ya ese contenido en memoria antes de usarlo.

En la antigüedad, teníamos siempre mecanismos de Callbacks a los que nos suscribíamos de manera que cuando el hilo nuevo terminaba lanzaba un evento y nosotros actuábamos en virtud a ello, asignándole un manejador. En lo personal, puedo decir que aunque es algo que funciona, no es muy natural, por el mismo hecho de que hay que agregar manejadores de eventos y demás.

No sería más natural hacer el llamado al método y saber que cuando la ejecución pase a la siguiente línea es porque el método anterior ya se ejecutó y lo mejor de todo sin bloquear la interfaz?

Bueno, para eso está la otra palabrita mágica protagonista de este post: await o sea, esperar. Si ponemos la palabra await antes del llamado a un método asíncrono, esto le dice al sistema que hasta que no acabe de ejecutarse el método llamado, la ejecución no puede avanzar hacia la siguiente línea. Lográndose entonces una ejecución natural sin tener que crear hilos ni objetos extraños e insisto: Sin bloquear la interfaz:

await ProcedimientoAsincrono();
SiguienteProcedimiento();

Tengan muy en cuenta sin embargo, que no siempre vamos a querer este comportamiento.

A veces por ejemplo requeriremos que se inicie el hilo y corra por su cuenta, mientras otra cosa pasa sin tener que esperar a que termine el procedimiento asíncrono. Por ejemplo, supongan que mientras cargan muchos datos, ponen una animación para entretener al usuario. En ese caso, lo que se debe hacer es lanzar el procedimiento asíncrono sin el await y luego lanzar la animación. De lo contrario, la animación solo comenzará a ejecutarse cuando el asíncrono haya terminado, lo que claramente no es nuestro objetivo. Así que es bueno tener una variable de finalización que sea actualizada por el método asíncrono. La animación entonces chequeará el valor de esta variable y solo se detendrá hasta que observe que la variable tiene el valor adecuado. Cuando este es nuestro objetivo, omitimos entonces el await en el llamado.

Una vez entendidos los conceptos de async y await, pensemos en lo siguiente:

Qué pasa si debemos llamar a un método que no fue creado como async?

Nos veríamos obligados a usar threading o background workers? No. Ni siquiera tenemos acceso a ellos desde el perfil del Framework disponible en WinRT. En este caso, regresamos un poco al Framework 4.0 donde con TPL (Task Parallel Library) Tenemos acceso a la clase Task que nos permite de inmediato ejecutar un proceso en otro hilo; cosa muy útil, cuando este proceso no ha sido declarado como asíncrono.

Entonces solo basta decirle a Task que ejecute el proceso que queremos:

Task.Run(()=>ProcedimientoNoAsincrono());
SiguienteProcedimiento();

Como se observa, Task.Run recibe una Action, que no es más que una Lambda apuntando al procedimiento no asíncrono que existe en algún otro sitio. El procedimiento llamado por Task, también puede ser asíncrono, pero en este caso es más fácil llamarlo directamente, como vimos arriba.

El llamado a Task también es susceptible a ser esperado usando await, así que obtenemos las mismas ventajas:
No se bloque la interfaz y decidimos si trabajar en paralelo o de manera síncrona.

En conclusión vimos qué nos ofrece el Framework .NET 4.5 para generar interfaces de usuario rápidas y fluidas. En el proceso, comprendimos sin muchas complicaciones para qué son y cómo se usan estos tres sujetos: async, await y task.

Para ver async y await en acción, les dejo este post, donde capturamos imágenes desde la cámara de un dispositivo con Windows 8.


asincronía, asincrónico, paralelo, hilos, threads, background, fluido, interfaz fluida, fast, framework 4.5, Windows 8,  metro style, apps, winRT, WindowsRT