Dans un des articles que j’ai publié sur MSDN, j’ai montré la manière d’exécuter en mode asynchrone une demande de traduction d’un texte à partir du service Web Microsoft Translator.

(Pour lire l’article en C# http://msdn.microsoft.com/fr-fr/windows/msdn.microsoft.translator.vcsharp.aspx ou en VB http://msdn.microsoft.com/fr-fr/windows/msdn.microsoft.translator.vb.aspx)

Rien de bien compliqué, puisque j’utilise le modèle à base d’événements (EAP Event Asynchronus Pattern).

Le client s’abonne à l’évènement.
_clientTranslator.TranslateCompleted += new EventHandler<TranslateCompletedEventArgs>(_clientTranslator_TranslateCompleted);

Puis appelle la méthode Asynchrone du service Web
private void cmdTraduire_Click(object sender, RoutedEventArgs e)
{
_clientTranslator.TranslateAsync(APPID, texte, de, vers);
}


Enfin, il traite et affiche les données dans la méthode de rappel _clientTranslator_TranslateCompleted
void _clientTranslator_TranslateCompleted(object sender, TranslateCompletedEventArgs e)
{
String Resultat=String.Empty;
if (e.Cancelled)
{
MessageBox.Show("Requête annulée") ;
}
if (e.Error != null)
{
MessageBox.Show(e.Error.Message) ;
}
else
{
MaTextBox.Text e.Result;
}
}

Bien que ce modèle soit des plus simples à comprendre, il n’en reste pas moins, que nous aimerions pouvoir écrire du code d’une manière plus naturelle et plus concise.
En un mot écrire du code séquentielle, mais avec des méthodes qui s’exécutent en mode asynchrone.

C’est la ou entre en jeu, le modèle de programmation asynchrone pour VB et C#, disponible depuis peu.
Pour obtenir de la documentation, des vidéos et le produit :
http://msdn.microsoft.com/en-us/vstudio/async.aspx

Deux mots clés, async et await ont été rajoutés aux langages VB et C# que l’on utilisera alors de la manière suivante :
(Remarque : ces deux mots clés ne seront disponibles qui si vous référencez  la librairie AsyncCTPLibrary.dll disponible avec la CTP)

private async void cmdTraduire_Click(object sender, RoutedEventArgs e)
{
        txtTraduit.Text = await TraduireAsync(“Texte à trduire”, "fr", "en");
 }


En ajoutant le mot clé async à la fonction cmdTraduire_click, on la déclare alors explicitement comme étant une fonction asynchrone ou plutôt on défini un contexte asynchrone, puis, on utilise le mot clé await devant l’appel de la fonction asynchrone elle même. Les deux mots clés vont de concert.
Il n’est pas possible d’utiliser l’un sans l’autre.

Le mot clé await, comme son nom l’indique, permet d’attendre le retour de la fonction TraduireAsync().  Cela rend la syntaxe simple et élégante à utiliser. C’est en faite le compilateur qui va construire toute la mécanique d’attente pour nous.

Il faut enfin définir la fonction asynchrone TraduireAsync, par exemple, de la manière suivante :

public Task<String> TraduireAsync(String texte, String de, String vers)             
{
Task<String> T=new Task<String>(()=>
   {
return _clientTranslator.Translate (APPID,texte,de,vers);
   });
   T.Start();
return T;
 }


Pour exécuter en mode asynchrone ma méthode de mon service Web, cette fois-ci je n’utilise pas directement la méthode asynchrone de celui-ci, mais sa méthode synchrone que j’encapsule dans une Task<String>.
Remarque : Le type de retour de la fonction asynchrone, est soit une Task<T>, soit une Task, soit un void comme indiqué dans la documentation.

Si on souhaite utiliser la méthode asynchrone du service Web, notre fonction TraduireAsync, ce complique légèrement.

public Task<String> TraduireAsync(String texte, String de, String vers)        
{
  Task<String> T = new Task<String>(() =>
  {
    EventHandler<TranslateCompletedEventArgs> completedHandler = null;
    TaskCompletionSource<String> tcs = new TaskCompletionSource<String>(TaskCreationOptions.AttachedToParent);
    completedHandler = delegate(object sender, TranslateCompletedEventArgs e)         {             if (e.Cancelled)                  {                  tcs.TrySetCanceled();                  }             else if (e.Error != null)                  {
                  tcs.TrySetException(e.Error);                   }             else                  {
                 tcs.TrySetResult(e.Result);                  }
_clientTranslator.TranslateCompleted -= completedHandler;
        };  _clientTranslator.TranslateCompleted += completedHandler;
 _clientTranslator.TranslateAsync(APPID, texte, de, vers);
      return tcs.Task.Result;     });  T.Start();  return T; }

Je ne rentre pas dans le détail de toute l’implémentation, mais on voit ici que j’utilise la classe TaskCompletionSource, pour représenter les opérations du modèle à base d’événements (EAP).

Vous allez me dire, c’est quoi l’arnaque ici ? C’est en faite plus compliqué à écrire, que d’utiliser directement le modèle EAP, et je répondrais OUI vous avez entièrement raison.

Le modèle async et await, reste très simple à utiliser, à partir du moment où les méthodes asynchrones sont déjà prévues. Il n’y a jamais vraiment de miracle en programmation.
Il faut donc comprendre et lire entre les lignes, nous aurons les développeurs qui vont utiliser les mots clés async et await et ceux qui développerons des librairies et des méthodes asynchrones pour les premiers.

C’est pourquoi dans la librairie AsyncCTPLibrary.dll, vous retrouvez pléthore de méthodes d’extension comme DownloadFileTaskAsync, méthode d’extension de la classe WebClient, CopyToAsync,
méthode d’extension de la classe Stream, ou encore SpeakTaskAsync méthode d’extension SpeechSynthetizer et plein d’autres encore, ainsi que des méthodes d’aide pour manipuler simplement des tasks.

Dans notre exemple, on pourrait imaginer que dans un futur proche, lorsque je crée le proxy de mon service Web avec Visual Studio, ce n’est pas le modèle EAP qui est crée par l’outil, mais directement le modèle asynchrone.

Eric Vernié