miércoles, 11 de mayo de 2016

Async & await… otra explicación


Ha pasado algún tiempo desde que los bonitos palabros aparecieron ( ¿fue en Visual Studio 2012? ), yo pensaba que todo desarrollador .NET que se preciase de serlo los tenía claro, pero el otro día una conversación en el trabajo me hizo ver la cruda realidad. Vamos a ver si mi granito de arena ayuda:

Async: indica que un método puede ejecutarse en modo asíncrono, y ojo, digo puede, porque que lo haga o no dependerá de la presencia del otro palabro.
Un método marcado como async debe obligatoriamente devolver un Task o bien nada (void)

Ojo!  Debemos evitar funciones async que no devuelvan nada (void). Existen para soportar event handlers asíncronos... en otros escenarios evitémoslo. Mas detalles aquí: https://channel9.msdn.com/Blogs/channel9spain/Async-best-practices-por-Llus-Franco

Await: Cuando dentro de un método asíncrono llamamos a await estamos indicando dos cosas:
  • Queremos esperar por el resultado de la ejecución de un task
  • Mientras estamos esperando devolvemos el control a nuestro llamante

Mirad el siguiente ejemplo, es sencillo:

class Program
    {
        static void Main(string[] args)
        {
            FooAsync();
            Console.WriteLine("Main continues execution...");
            Console.ReadLine();            
        }

        public static async void FooAsync()
        {            
            Console.WriteLine("Foo is going to call ExecuteLongRunningTask...");
            Task<string> t = ExecuteLongRunningTask();

            Console.WriteLine("Foo continues execution...");
            Thread.Sleep(1000);
            Console.WriteLine("Foo is going to wait for ExecuteLongRunningTask response...");

            var s = await t;
            Console.WriteLine("Foo got the response: {0}", s);
        }

        public static Task<string> ExecuteLongRunningTask()
        {            
            return (Task.Factory.StartNew(() => 
                    { 
                        Thread.Sleep(5000); 
                        return "Hi all!!"; 
                    }));
        }        
    }


En cristiano:

  • Main llama a FooAsync
  • FooAsync llama a un metodo que genera una tarea que tardará en ejecutarse (5 segundos), De momento main sigue bloqueado
  • Cuando FooAsync llame a await empezará la magia:  main continuará su ejecución  mientras que FooAsync estará bloqueado hasta que la tarea finaliza su ejecución

Si lo ejecuto tendré esto:

Untitled2

Dicen que una imagen vale mas que mil palabras:
Untitled

En resumen, hemos conseguido que el método FooAsync se ejecute en asíncrono, pero lo mejor es que es el propio método asíncrono el que decide que parte se ejecuta en asíncrono y que parte no.
Espero que sirva de ayuda…

Bonus: el código lo podeis encontrar en github: https://github.com/snavarropino/CSharp-Samples