[Windows Phone 7] la CTP Async par l’exemple: ça apporte quoi?

Lors du MIX11 il a été annoncé que la nouvelle CTP Async de Visual Studio supportait Windows Phone 7. Async est un framework qui apporte quelques éléments qui simplifient grandement la programmation asynchrone. Or comme on le sait quand on y a un peu touché, la très grande majorité des API du SDK Windows Phone sont asynchrones. Le problème du code asynchrone est qu’il est en général assez moche à regarder: ce n’est pas vraiment lisible, on passe en permanence d’une méthode à une autre, en induisant souvent des confusions et des problèmes d’accès entre threads, des delegate et des méthodes anonyme de partout, ça devient vite un gros plat de spaghetti.

Sans en faire un tour complet un peu long, il suffit de s’intéresser aux deux mots clefs apportés par cette CTP Async pour en comprendre la génialitude: async, et await. Ces deux mots clefs permettent d’écrire de façon séquentielle un code asynchrone. Pour faire un exemple simple, nous allons prendre pour exemple un simple appel à un WebClient pour le classique client Twitter qui semble avoir remplacé ce bon vieux Hello World…

Normalement, voila comment on écrit un appel à WebClient: on l’instancie, on s’abonne à l’évènement de complétion du téléchargement, et ensuite on lance le téléchargement. Le code qui est associé à l’event handler de complétion s’exécutera quand le téléchargement sera fini, et donc ça ressemble à ça:

 private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
{
    WebClient wc = new WebClient();
    wc.DownloadStringCompleted += new DownloadStringCompletedEventHandler(wc_DownloadStringCompleted);
    wc.DownloadStringAsync(new Uri("https://api.twitter.com/1/statuses/user_timeline.xml?screen_name=pierreca"));
}

void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
    listBox1.ItemsSource = from tweet in XElement.Parse(e.Result).Descendants("status")
                            select new Tweet()
                            {
                                PictureUrl = tweet.Element("user").Element("profile_image_url").Value.ToString(),
                                Text = tweet.Element("text").Value.ToString(),
                                UserName = tweet.Element("user").Element("screen_name").Value.ToString()
                            };
}

Maintenant avec la CTP Async on peut utiliser les mots clefs async et await pour tout écrire d’un seul bloc linéaire. D’abord on commence par signaler qu’une méthode va contenir du code asynchrone en la préfixant du mot clef async. Ensuite dans le corps de la méthode on utilise le mot clef await pour signaler que tout ce qui suivra l’appel préfixé de await sera exécuté au retour de cet appel… comme si le code était bloquant, sauf qu’il ne l’est pas Sourire Le bout de code suivant est donc équivalent au précédent:

 private async void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
{
    WebClient wc = new WebClient();
    var items = await wc.DownloadStringTaskAsync(new Uri("https://api.twitter.com/1/statuses/user_timeline.xml?screen_name=pierreca"));

    listBox1.ItemsSource = from tweet in XElement.Parse(items).Descendants("status")
                            select new Tweet()
                            {
                                PictureUrl = tweet.Element("user").Element("profile_image_url").Value.ToString(),
                                Text = tweet.Element("text").Value.ToString(),
                                UserName = tweet.Element("user").Element("screen_name").Value.ToString()
                            };
}

Beaucoup plus lisible à mon humble avis!

Les plus observateurs d’entre vous auront remarqué un changement que je n’ai pas signalé… on appelle maintenant la méthode WebClient.DownloadStringTaskAsync plutôt que WebClient.DownloadStringAsync. C’est une méthode d’extension qui est amené par la CTP et qui est renvoie Task<string>.. au lieu de void! En effet, il faut bien pouvoir récupérer le résultat de cet appel à await… qui va générer une Task dont le résultat est une chaine de caractères… l’utilisation de async et await n’est donc pas complètement transparente dans votre code: il faudra également modifier un peu vos prototypes de méthodes mais c’est le prix à payer pour bénéficier de l’élégance de ces nouveaux mots clefs!

Maintenant que vous êtes convaincus que cette CTP est géniale, sachez que vous pouvez la télécharger ici. Attention à bien prendre la version “Refresh” pour être compatible avec le SP1 de Visual Studio Sourire Ensuite dans vos projets, n’oubliez pas de référencer l’assembly AsyncCtpLibrary_Phone.dll qui se trouve dans votre dossier documents, dans \Microsoft Visual Studio Async CTP\Samples.

Quid de Rx?

Rx et Async sont bien deux frameworks permettant d’écrire élégamment du code asynchrone, mais ils ne sont pas du tout voués aux même usages. Rx implémente le pattern duale de l’IEnumerable à savoir l’IObservable : au lieu d’itérer activement dans une collection, on s’abonne aux ajouts d’éléments dans cette collection. Rx permet donc de gérer des suites d’évènements et avec une syntaxe fonctionnelle commence même à ressembler à une espèce de “Linq to Events”. L’utilité de Rx est donc dans la gestion de scénarii asynchrones complexes impliquant par exemple plusieurs sources d’évènements, le besoin de gérer l’élément temps, etc. La CTP async a un but beaucoup plus modeste: tout simplement simplifier l’écriture du code et en améliorer la lisibilité.