CTP : Le modèle de programmation asynchrone C# et VB

 

  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# https://msdn.microsoft.com/fr-fr/windows/msdn.microsoft.translator.vcsharp.aspx ou en VB https://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 : https://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é