Dot.Blog

C#, XAML, WinUI, WPF, Android, MAUI, IoT, IA, ChatGPT, Prompt Engineering

Simplifier l’approche des Async Void

Tout le monde le sait ou au moins le dit, écrire des méthodes Async retournant Void est une mauvaise idée…Mais comment faire alors ?

Les méthodes Async Void c’est mal

Alors je ne vais pas entrer dans ce débat qui a déjà fait couler beaucoup d’encre mais pour résumer écrire des méthodes Async qui retournent Void c’est une mauvaise idée. On peut lister entre autres raisons :

  • Si une exception se produit et qu’elle n’est pas traitée par le code Async, adios amigos, l’App est morte…
  • Dans le meilleur des cas en debug vous n’arriverez pas à obtenir une pile d’appel pour vous aider à trouver le problème et c’est gênant…
  • Au passage on notera que cela provoque des Warnings à la compilation et trop de Warnings tue les Warnings, tout le monde s’en fiche, personne ne les lit plus alors que la bonne programmation c’est zéro Warnings ! (C’est dit même si ça peut fâcher !)

Tout cela va rendre votre App plus fragile, plus difficile à déboguer, elle va planter et vous ne saurez pas comment vous en sortir. Vous aurez le client sur le dos, votre patron sur le dos, et vous finirez dans un carton dehors en jouant de la guitare avec un vieux chien pour espérer remplir votre sébile… C’est triste, vous n’avez pas envie de terminer comme ça si jeune. Alors Stop aux méthodes Async Void !

Si vous voulez aller plus loin dans l’argumentation je vous conseille cet article de John Thiriet “Removing async void”. Il y a d’autres articles sur la question si vous voulez allez encore plus loin.

En tout cas ici et pousser l’analyse plus avant on considérera que les Async Void sont l’une des représentations du mal en programmation C#. On les évite, on fait autrement.

TaskMonitor

Faire autrement c’est bien joli mais ce n’est pas toujours évident… C’est pourquoi un peu d’aide ne fera pas de mal.

Cette aide s’appelle ici TaskMonitor, c’est un Nuget à installer (dont le code se trouve ici https://github.com/roubachof/Sharpnado.TaskMonitor). C'est du .NET Standard 2.0 donc compatible avec Core 5, 6, 7 etc (Microsoft a indiquer que .NET Standard 2.0 et 2.1 seraient toujours supportés dans .NET Core).

En gros ce code a été inspiré par NotifyTask de Stephen Cleary, un petit “gadget” pour l’Asynchronisme qui se trouve dans une librairie avec d’autres amis, Mvvm.Async. Je n’en dirai pas plus étant donné que ce n’est pas le sujet mais cela vaut le détour si vous avez quelques minutes à consacrer à ces petits helpers consacrés à la simplification du code Async.

Bref TaskMonitor a une histoire, des cousins, mais il est plus généraliste et peut s’utiliser dans de nombreux contextes (partie serveur, business layer, traitement de l’UI, etc).

Ses principales fonctions :

  • Exécution sécurisée de toutes tâches Async, parfait pour les Async Void et les tâches non attendues (lorsqu’on a une Task qui retourne quelque chose mais qu’on ne récupère pas ce quelque chose).
  • Il offre des Callbacks pour tous les états (annulé, réussi, terminé, erreur)
  • Il permet d’utiliser des gestionnaires d’erreur personnalisés mais propose des implémentations par défaut
  • Il offre aussi un logger de stats pour les tâches, là encore personnalisable ou en utilisant l’implémentation par défaut.

Bref ça fait plein de choses mais surtout c’est très bien conçu pour les Async Void et les tâches non attendues. Code qu’on trouve encore trop souvent malgré ses dangers (voir en début d’article).

Comment se libérer des Async Void ?

J’ai repris l’exemple donné par Jean-Marie Alfonsi qui a écrit les extensions connues sous le nom de “Sharpnado” et qu’on trouve sur Github ici : https://github.com/roubachof. Vous pouvez d’ailleurs lire son article en anglais si vous préférez l’original car bien entendu dans un tel cas je n’invente rien, tout ce que je dis, en dehors de mes digressions personnelles, je le puise forcément de quelque part… La nuit moi le Grand Architecte ne vient pas me dire en rêve ce que je dois écrire le lendemain je n’ai pas cette chance. Donc je me documente. Résumant ainsi ma démarche “si je le sais, c’est que je l’ai lu quelque part”.

Son exemple est troublant d’évidence. Imaginez un instant que par malchance vous ayez un méchant Async Void dans votre code (promis on accepte l’excuse que c’est du code legacy et que vous n’y êtes pour rien) :

public async void InitializeAsync()  
{
    try
    {
        await InitializeMonkeyAsync();
    }
    catch (Exception exception)
    {
        // handle error
    }
}

private async Task InitializeMonkeyAsync()  
{
    Monkey = await _monkeyService.GetMonkeyAsync();
}

Comment s’en sortir sans tout réécrire et pour rétablir la sécurité de l’ensemble et la possibilité de débogue en cas de pépin ?

Comme ça :

public void InitializeAsync()  
{
    TaskMonitor.Create(InitializeMonkeyAsync);
}

Vous avez vu l’astuce ? L’appel à la méthode Async Void est en paramètre de TaskMonitor.Create(). Et l’affaire est jouée !

S’il y a une exception elle sera tracée par le gestionnaire par défaut fourni par TaskMonitor ce qui vous permettra de déboguer. Sinon l’App cracherait (adios) ou s’arrêtera silencieusement (adios avec des chaussons).

Il reste possible de fournir son propre gestionnaire d’erreur sous la forme d’une lambda :

TaskMonitorConfiguration.ErrorHandler = (message, exception) =>  
    {
        // Do custom stuff for exception handling;
    };

Conclusion

TaskMonitor c’est vraiment bien, cela fait revenir un peu de sécurité et de raison, et de maintenabilité, dans vos Apps. Pour l’exemple sur le code non attendu ou le les statistiques des tâches et tout le reste je vous renvoie à l’article original même s’il est en anglais, car il est très riche de références qui valent le détour.

Dans tous les cas vous savez maintenant au moins comment vous sortir de ce mauvais pas que sont les Async Void et autres code non attendus…

Stay Tuned !

Faites des heureux, partagez l'article !
blog comments powered by Disqus