Etiquetado: Async

¿Como esperar con await en una expresión lambda?

Hoy he necesitado escribir un código dentro de una lambda que llamaba a métodos asíncronos. Normalmente estamos acostumbrados a marcar métodos asíncronos con la palabra async y ya podemos usar await a diestro y siniestro, pero ¿cómo se hace con una lambda? Pues en realidad igual, marcando la lambda con async:

Task.Factory.StartNew(async () =>
{
    await Task.Delay(1000);
});

Y por supuesto, la función lambda también es awaitable:

Func<Task<string>> t = async () =>
{
    await Task.Delay(1000);
    return "Hello";
};
System.Diagnostics.Debug.WriteLine(await t());

Por cierto, id con cuidado con ésto último que he hecho, poner un await como argumento, pues puede que nos llevemos alguna sorpresa… pero ya os lo cuento en otro mini-post.

Anuncios

Consultas asíncronas LINQ mediante extensiones

Una parte que falta por ahora en LINQ es una forma directa de ejecutar una query de forma asíncrona.
Si tenemos una query cualquiera como por ejemplo:

var query = from media in context.MediaFiles
           select new MediaItem
           {
               Title=media.Title,
               Path = media.Path,
               MimeType = media.MimeType,
               Date=media.CreationTime,
               Size=media.Length
           };

Para ejecutar la consulta en modo asíncrono no tenemos ningún método asíncrono directamente (no confundir con paralelo como en PLINQ) y tenemos que meterlo en una Task:

var result = await Task<List<MediaItem>>.Run(() =>
{
    return query.ToList();
});

Para no tener que escribir tantas veces la creación de Task podemos meter ese código en un método de extensión, gracias a que tenemos generics:

public static class QueryExtensions
{
    public static Task<List<T>> ToListAsync<T>(this IQueryable<T> query)
    {
        return Task<List<T>>.Run(() =>
        {
           return query.ToList();
        });
    }
}

Como bien dice Kenneth en el comentario, en la extensión no nos hace falta el async/await, pues la Task es por definición “awaitable”

Ahora ya podemos escribir la llamada de forma más clara:

var mediaFiles = await query.ToListAsync();

Con este sistema podremos ir creando todos los que necesitemos en una clase de extensión para su uso con cualquier elemento IQueryable.

Patrón async / await cuando tenemos eventos (EAP)

Cuando te acostumbras a usar await lo echas en falta si el API que usas no lo tiene. Esto mismo le pasó a Darío con el nuevo LiveSDK.

La buena noticia es que podemos encapsular en una Task nuestras llamadas asíncronas que usan el modelo basado en eventos (EAP).

En el caso de LiveSDK vamos a partir del ejemplo de Michael Crump. En él nos enseña a utilizar LiveSDK en una aplicación Metro de Windows 8, pero sigue utilizando el modelo EAP.

this.liveClient = new LiveConnectClient(e.Session);
session = e.Session;
this.liveClient.GetCompleted += OnGetCompleted;
this.liveClient.GetAsync("me", null);

Convertirlo al nuevo patrón asíncrono es bastante sencillo utilizando una combinación de Task y TaskCompletionSource, para hacerlo un poco más interesante lo escribiremos como método de extensión:

public static class LiveExtensions
{
    public static Task<IDictionary<string, object>>
        GetAsyncEx(this LiveConnectClient client, string path, object state)
    {
        var tcs = new TaskCompletionSource<IDictionary<string, object>>();
        client.GetCompleted += (o, e) =>
        {
            if (e.Error != null)
                tcs.SetException(e.Error);
            else
                tcs.SetResult(e.Result);
        };
        client.GetAsync(path, state);
        return tcs.Task;
    }
}

Una vez tenemos el método así escrito ya podemos usar await y reescribir el código original con un flujo mucho más claro 🙂

private async void btnLogIn_OnSessionChanged(object sender, 
    LiveConnectSessionChangedEventArgs e)
{
    if (e.Session != null && e.Status == LiveConnectSessionStatus.Connected)
    {
        this.liveClient = new LiveConnectClient(e.Session);
        session = e.Session;
        //this.liveClient.GetCompleted += OnGetCompleted;
        //this.liveClient.GetAsync("me", null);
        try
        {
            dynamic result = await this.liveClient.GetAsyncEx("me", null);
            this.tbName.Text = "Hello, " + result.first_name + " " + result.last_name;
            this.tbGender.Text = "You are a " + result.gender + " that lives in " + 
                result.locale + ".";
            this.tbLiveProfile.Text = "Your Live Profile can be found at: " + result.link;
        }
        catch (Exception ex)
        {
            this.tbError.Text = ex.ToString();
        }
    }
    else
    {
        this.liveClient = null;
    }
}

Para profundizar más sobre el patrón os recomiendo leer el documento que escribió Stephen Toub sobre el tema: Task-based Asynchronous Pattern (TAP).

Async CTP y Windows Phone 7.5

Tal como os comentaba en mi artículo sobre Async y Windows Phone, la Async CTP de abril no funcionaba con el SDK de Windows Phone 7.1, así que nos quedamos con las ganas de poder utilizar esta potente librería en nuestro dispositivo favorito.

Bien, pues durante el tiempo que he estado sin actualizar mi blog han ocurrido muchas cosas, entre ellas: en Noviembre apareció la v3 de la Async CTP, que ya tiene soporte para Silverlight 5 y Windows Phone 7.5.  Tenéis más detalles en el post de Stephen Toub.

Si queréis probar Async en todo su esplendor, os recomiendo descargar Windows 8 Developer Preview con las herramientas, incluye una versión “preview” de Visual Studio 11 con el Framework 4.5 que lleva Async de serie y los métodos asíncronos son una parte muy importante del desarrollo de aplicaciones Metro en Windows 8.

Feliz Año 7DC