Gestion des exceptions de la partie synchrone de la méthode async

Je suis confronté à la situation où la tâche que je commence peut être lancée, tout en exécutant de manière synchrone le thread initial. Quelque chose comme ça, à des fins d’illustration:

static async Task TestAsync() { var random = new Random(Environment.TickCount).Next(); if (random % 2 != 0) throw new ApplicationException("1st"); await Task.Delay(2000); Console.WriteLine("after await Task.Delay"); throw new ApplicationException("2nd"); } 

À partir du code d’appel, j’aimerais pouvoir intercepter les exceptions éventuelles de la partie synchrone (c’est-à-dire jusqu’à l’ await Task.Delay() ). Voici comment je le fais actuellement:

 static void Main(ssortingng[] args) { try { var task = TestAsync(); if (task.IsFaulted) task.GetAwaiter().GetResult(); Console.WriteLine("TestAsync continues asynchronously..."); } catch (Exception e) { Console.WriteLine("Error: " + e.ToSsortingng()); } Console.WriteLine("Press Enter to exit..."); Console.ReadLine(); } 

Cela fonctionne, bien que cela ait l’air un peu bouillant, car il n’y a pas de Result sur la Task .

J’ai aussi essayé task.Wait() au lieu de task.GetAwaiter().GetResult() . Cela me donne toujours une AggregateException que je dois décompresser (plutôt que directement ApplicationException attendue).

Y a-t-il d’autres options?

[MODIFIÉ] Pour adresser les commentaires: je le fais, car si la tâche échoue instantanément, je ne veux pas l’append à la liste des tâches en attente que je gère. La tâche elle-même ne sait rien d’une telle liste (et ce n’est pas obligé). Je veux toujours enregistrer l’exception et en informer l’utilisateur. Je pourrais également throw task.Exception , mais cela ne donnerait pas le cadre de stack d’exception capturé avec ExceptionDispatchInfo .

[MISE À JOUR] Inspiré par d’autres réponses et commentaires: si j’ai le contrôle total de TestAsync et que je ne veux pas introduire de nouveaux membres de classe, je pourrais aussi faire quelque chose comme ci-dessous. Cela peut être utile lors de la validation des arguments:

 static Task TestAsync(int delay) { if (delay < 0) throw new ArgumentOutOfRangeException("delay"); Func asyncPart = async () => { Console.WriteLine("await Task.Delay"); await Task.Delay(delay); throw new ApplicationException("2nd"); }; return asyncPart(); } 

Je l’avais divisé en deux parties, plutôt que de compter sur task.GetAwaiter().GetResult() pour fonctionner. J’aurais peur que quelqu’un qui entretient TestAsync puisse involontairement casser des choses à l’avenir.

C’est comme ça que je l’écrirais. Cela devrait préserver le comportement que vous avez, mais je trouve plus évident ce qui se passe:

 static Task Test() { var random = new Random(Environment.TickCount).Next(); if (random % 2 != 0) throw new ApplicationException("1st"); return TestAsync(); } static async Task TestAsync() { await Task.Delay(2000); Console.WriteLine("after await Task.Delay"); throw new ApplicationException("2nd"); } static void Main(ssortingng[] args) { try { Test(); Console.WriteLine("TestAsync continues asynchronously..."); } catch (Exception e) { Console.WriteLine("Error: " + e.ToSsortingng()); } Console.WriteLine("Press Enter to exit..."); Console.ReadLine(); } 

De manière générale, les exceptions ne doivent pas être utilisées pour la gestion des erreurs de routine dans votre application. Lancez des exceptions pour des circonstances “exceptionnelles”, où le programme ne peut pas continuer parce qu’un imprévu s’est produit et que vous devez faire une pause.

Certes, je ne sais pas exactement quel est votre cas d’utilisation, mais chaque fois que j’utilise des tâches asynchrones, l’élément qui va échouer de manière inattendue est généralement aussi l’élément qui devrait être asynchrone (par exemple, la connexion à une firebase database).

Quoi qu’il en soit, je ferais en TestAsync de placer votre méthode TestAsync dans sa propre classe. Ensuite, vous pouvez avoir une méthode (ou une propriété) bool TestAsync.IsValid pour déterminer si la tâche est prête à être lancée et doit être mise en queue pour exécution; ensuite, vous pouvez exécuter votre tâche asynchrone si la réponse est vraie: TestAsync.RunAsync() .