Async/Await — Programmation asynchrone
Notions théoriques
Pourquoi l'asynchronie ?
Par défaut, C# exécute les instructions de façon synchrone : chaque ligne attend que la précédente se termine. Cela pose problème pour les opérations longues (appels réseau, lecture de fichiers, requêtes SQL) : pendant ce temps, le thread est bloqué et ne peut rien faire d'autre.
La programmation asynchrone permet de lancer une opération longue et de continuer à exécuter d'autre code en attendant le résultat. En C#, cela s'appuie sur les mots-clés async et await.
En ASP.NET Core, chaque requête HTTP utilise un thread. Si ce thread est bloqué à attendre une base de données, il ne peut pas traiter d'autres requêtes. L'asynchronie permet de servir beaucoup plus d'utilisateurs simultanément avec le même nombre de threads.
async et Task
Une méthode asynchrone est déclarée avec le mot-clé async. Elle retourne toujours un Task ou un Task<T> :
async Task: méthode qui ne retourne pas de valeur (équivalent devoid)async Task<T>: méthode qui retourne une valeur de typeT
// Méthode asynchrone sans retour
async Task SauvegarderAsync(Personnage p) { ... }
// Méthode asynchrone avec retour
async Task<List<Personnage>> ChargerTousAsync() { ... }
Le mot-clé await
await suspend l'exécution de la méthode courante jusqu'à ce que le Task soit terminé, sans bloquer le thread. La méthode reprend ensuite là où elle s'était arrêtée.
// Appel synchrone (bloque le thread - à éviter)
var personnages = service.ChargerTous();
// Appel asynchrone (libère le thread pendant l'attente)
var personnages = await service.ChargerTousAsync();
N'utilisez jamais .Result ou .Wait() pour attendre un Task. Ces appels bloquent le thread et peuvent provoquer des deadlocks dans des applications ASP.NET Core. Utilisez toujours await.
HttpClient pour les appels HTTP
HttpClient est la classe standard pour effectuer des requêtes HTTP asynchrones en C# :
using System.Net.Http;
using System.Net.Http.Json;
var client = new HttpClient();
var personnages = await client.GetFromJsonAsync<List<Personnage>>("https://api.exemple.com/personnages");
N'instanciez pas HttpClient dans une boucle : créez-en un seul par application (ou utilisez IHttpClientFactory dans ASP.NET Core).
Parallélisme avec Task.WhenAll
Pour lancer plusieurs opérations en parallèle et attendre qu'elles soient toutes terminées :
// Lance les deux requêtes en parallèle, attend que les deux soient finies
var tacheGuerriers = repo.ChargerParTypeAsync("Guerrier");
var tacheMages = repo.ChargerParTypeAsync("Mage");
await Task.WhenAll(tacheGuerriers, tacheMages);
var guerriers = tacheGuerriers.Result; // .Result est sûr APRÈS WhenAll
var mages = tacheMages.Result;