374 votes

Pourquoi utiliser async et renvoyer await, alors que vous pouvez renvoyer directement Task<T> ?

Y a-t-il tous scénario où l'on écrit une méthode comme celle-ci :

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

au lieu de cela :

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

aurait-elle un sens ?

Pourquoi utiliser return await alors que vous pouvez directement renvoyer Task<T> de l'intérieur DoAnotherThingAsync() invocation ?

Je vois un code avec return await dans tant d'endroits, je pense que j'ai peut-être raté quelque chose. Mais pour autant que je comprenne, ne pas utiliser les mots-clés async/await dans ce cas et renvoyer directement la tâche serait fonctionnellement équivalent. Pourquoi ajouter des frais généraux supplémentaires de await couche ?

7 votes

Je pense que la seule raison pour laquelle vous voyez cela est que les gens apprennent par imitation et qu'ils utilisent généralement (s'ils n'en ont pas besoin) la solution la plus simple qu'ils peuvent trouver. Donc les gens voient ce code, utilisent ce code, ils voient que ça marche et à partir de maintenant, pour eux, c'est la bonne façon de faire... Il ne sert à rien d'attendre dans ce cas.

21 votes

Il y a au moins une différence importante : propagation des exceptions .

1 votes

Je ne comprends pas non plus, je ne comprends pas du tout ce concept, il n'a aucun sens. D'après ce que j'ai appris, si une méthode a un type de retour, elle DOIT avoir un mot-clé return, n'est-ce pas la règle du langage C# ?

12voto

Andrew Arnott Points 35346

Le fait de rendre asynchrone la méthode "thunk", par ailleurs simple, crée une machine d'état asynchrone en mémoire, alors que la méthode non asynchrone n'en crée pas. Bien que cela incite souvent les gens à 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 "pile de retour/continuation", ce qui rend parfois plus difficile la compréhension du blocage.

Donc oui, quand la perf n'est pas critique (et elle ne l'est généralement pas), je lance async sur toutes ces méthodes thunk afin d'avoir la machine d'état async pour m'aider à diagnostiquer les blocages plus tard, et aussi pour s'assurer que si ces méthodes thunk évoluent dans le temps, elles seront sûres de renvoyer des tâches faussées au lieu de lancer des tâches.

5voto

heltonbiker Points 4725

Cela me trouble également et j'ai l'impression que les réponses précédentes n'ont pas tenu compte de votre véritable question :

Pourquoi utiliser la construction return await quand vous pouvez directement retourner Task à partir de l'invocation interne DoAnotherThingAsync() ?

Parfois, vous en fait veulent un Task<SomeType> mais la plupart du temps, vous voulez une instance de SomeType c'est-à-dire le résultat de la tâche.

A partir de votre code :

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

Une personne qui ne connaît pas la syntaxe (moi, par exemple) pourrait penser que cette méthode devrait renvoyer une valeur de Task<SomeResult> mais comme il est marqué d'un async cela signifie que son type de retour réel est SomeResult . Si vous n'utilisez que return foo.DoAnotherThingAsync() vous retourneriez une Task, ce qui ne compilerait pas. La bonne méthode consiste à renvoyer le résultat de la tâche, donc la méthode return await .

3voto

ssg Points 20321

Une autre raison de vouloir return await : Le await La syntaxe vous permet d'éviter les problèmes de concordance entre Task<T> y ValueTask<T> types. Par exemple, le code ci-dessous fonctionne même si la méthode SubTask renvoie Task<T> mais l'appelant renvoie ValueTask<T> .

async Task<T> SubTask()
{
...
}

async ValueTask<T> DoSomething()
{
  await UnimportantTask();
  return await SubTask();
}

Si vous n'attendez pas sur le DoSomething() vous obtiendrez une erreur de compilation CS0029 :

Impossible de convertir implicitement le type 'System.Threading.Tasks.Task<BlaBla>' en 'System.Threading.Tasks.ValueTask<BlaBla>'.

Vous obtiendrez également CS0030 si vous essayez de le typer explicitement.

Il s'agit du .NET Framework, soit dit en passant. Je peux tout à fait prévoir un commentaire disant "c'est corrigé dans .NET version_hypothétique "Je ne l'ai pas testé :)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X