Quel est le but du retour en attente dans C #?

Y a-t-il un scénario où la méthode d’écriture est la suivante:

public async Task DoSomethingAsync() { // Some synchronous code might or might not be here... // return await DoAnotherThingAsync(); } 

au lieu de cela:

 public Task DoSomethingAsync() { // Some synchronous code might or might not be here... // return DoAnotherThingAsync(); } 

aurait du sens?

Pourquoi utiliser return await construct lorsque vous pouvez retourner directement Task partir de l’ DoAnotherThingAsync() interne DoAnotherThingAsync() ?

Je vois du code avec return await dans tant d’endroits, je pense que j’aurais dû manquer quelque chose. Mais pour autant que je sache, utiliser des mots-clés asynchrones / d’attente dans ce cas et renvoyer directement la tâche serait équivalent sur le plan fonctionnel. Pourquoi append une surcharge supplémentaire de la couche d’ await supplémentaire?

Il y a un cas sournois où le return en méthode normale et le return await dans la méthode async se comportent différemment: lorsqu’il est combiné avec l’ using (ou, plus généralement, tout return await dans un bloc try ).

Considérons ces deux versions d’une méthode:

 Task DoSomethingAsync() { using (var foo = new Foo()) { return foo.DoAnotherThingAsync(); } } async Task DoSomethingAsync() { using (var foo = new Foo()) { return await foo.DoAnotherThingAsync(); } } 

La première méthode sera Dispose() l’object Foo dès que la méthode DoAnotherThingAsync() , ce qui est probablement long avant qu’elle ne soit réellement terminée. Cela signifie que la première version est probablement boguée (parce que Foo est disposé trop tôt), tandis que la deuxième version fonctionnera correctement.

Si vous n’avez pas besoin d’ async (c.-à-d. Que vous pouvez retourner la Task directement), n’utilisez pas async .

Il y a des situations où return await est utile, comme si vous avez deux opérations asynchrones à faire:

 var intermediate = await FirstAsync(); return await SecondAwait(intermediate); 

Pour plus d’informations sur les performances async , consultez l’ article MSDN et la vidéo de Stephen Toub sur le sujet.

Mise à jour: J’ai écrit un article de blog qui contient beaucoup plus de détails.

La seule raison pour laquelle vous voudriez le faire, c’est s’il y a une autre await dans le code précédent, ou si vous manipulez le résultat avant de le renvoyer. Une autre façon de procéder est d’utiliser un try/catch qui modifie la gestion des exceptions. Si vous ne faites rien de tout cela, vous avez raison, il n’y a pas de raison d’append la méthode async la méthode.

Un autre cas que vous pourriez avoir besoin d’attendre le résultat est celui-ci:

 async Task GetIFooAsync() { return await GetFooAsync(); } async Task GetFooAsync() { var foo = await CreateFooAsync(); await foo.InitializeAsync(); return foo; } 

Dans ce cas, GetIFooAsync() doit attendre le résultat de GetFooAsync car le type de T est différent entre les deux méthodes et Task n’est pas directement assignable à la Task . Mais si vous attendez le résultat, cela devient juste Foo qui est directement assignable à IFoo . Ensuite, la méthode asynchrone ne fait que reconditionner le résultat dans Task et c’est parti.

Faire async la méthode “thunk”, par ailleurs simple, crée une machine à états asynchrone en mémoire alors que la méthode non asynchrone ne le fait pas. Bien que cela puisse souvent indiquer aux gens d’utiliser la version non asynchrone parce qu’elle est plus efficace (ce qui est vrai), cela signifie également qu’en cas de blocage, vous n’avez aucune preuve que cette méthode est impliquée dans la stack “return / continuation” ce qui le rend parfois plus difficile à comprendre.

Donc, oui, quand perf n’est pas critique (et ce n’est généralement pas le cas), je lance async sur toutes ces méthodes afin d’avoir la machine d’état asynchrone pour m’aider à diagnostiquer plus tard, et aussi les méthodes de thunk évoluent avec le temps, elles seront sûres de renvoyer des tâches défectueuses au lieu de lancer.

Cela me déroute aussi et j’estime que les réponses précédentes ont négligé votre question réelle:

Pourquoi utiliser l’attente de retour lorsque vous pouvez retourner directement la tâche à partir de l’appel interne DoAnotherThingAsync ()?

Eh bien, parfois, vous voulez réellement une Task , mais la plupart du temps, vous voulez réellement une instance de SomeType , c’est-à-dire le résultat de la tâche.

De votre code:

 async Task DoSomethingAsync() { using (var foo = new Foo()) { return await foo.DoAnotherThingAsync(); } } 

Une personne peu familière avec la syntaxe (moi, par exemple) peut penser que cette méthode doit renvoyer une Task , mais comme elle est marquée par async , cela signifie que son type de retour est SomeResult . Si vous utilisez simplement return foo.DoAnotherThingAsync() , vous retournerez une tâche qui ne sera pas compilée. La manière correcte est de renvoyer le résultat de la tâche pour que le return await .