26

Suppose I have a C# controller that calls into some arbitrary function that returns a Task (for instance because it performs a database transaction). Should I always use async and await, or should I just return the task?

Example controller:

public async Task<string> DoSomething() {
    return await SomeOtherFunctionThatReturnsATask();
}

Should I change this into:

public Task<string> DoSomething() {
    return SomeOtherFunctionThatReturnsATask();
}

Or does it really not matter?

Robba
  • 7,684
  • 12
  • 48
  • 76
  • No. You don't need to do this for methods, unless you want to use the Task's return value in the method itself. – Panagiotis Kanavos Feb 04 '16 at 13:17
  • 4
    [Related question](http://stackoverflow.com/questions/19098143/what-is-the-purpose-of-return-await-in-c) and [cost of async await](https://msdn.microsoft.com/en-us/magazine/hh456402.aspx) – Sriram Sakthivel Feb 04 '16 at 13:18

2 Answers2

34

Yes, you should change the method and remove the async/await. The async keyword causes the compiler to create a state machine that manages the 'waiting' for the task to complete. When you await another function like that, you're essentially creating two of these state machines which is unnecessary. It's much better to just return the task from the second function directly and allow the final consumer of the task to do the waiting.

The best way to understand this, is to write a small sample program and decompile it. Make sure your decompiler is showing you all the compiler generated stuff (which some hide by default) and you will be able to see what all is going on there.

Here's a quick example I just whipped up and used dotPeek to decompile:

public Task<string> DoSomething()
{
  Class1.\u003CDoSomething\u003Ed__0 stateMachine;
  stateMachine.\u003C\u003E4__this = this;
  stateMachine.\u003C\u003Et__builder = AsyncTaskMethodBuilder<string>.Create();
  stateMachine.\u003C\u003E1__state = -1;
  stateMachine.\u003C\u003Et__builder.Start<Class1.\u003CDoSomething\u003Ed__0>(ref stateMachine);
  return stateMachine.\u003C\u003Et__builder.Task;
}

private Task<string> DoSomethingElse()
{
  return Task.FromResult<string>("test");
}

You can see the state machine in the first one that I was referring to. It's going to do all that work of await'ing for no reason, and then the final consumer of DoSomething() is going to repeat that same work. Realistically you should only really use the await keyword when there is other code in the method that needs to be run after the code that returns the task. Because that code needs to await for it to complete before it runs.

Full decompiled code here: http://pastebin.com/iJLAFdHZ

CodingGorilla
  • 19,612
  • 4
  • 45
  • 65
  • Thanks alot! I assume this is also the reason why any exceptions thrown always have dozens of TaskAwaiter sections in them. – Robba Feb 04 '16 at 13:39
  • @MatthewWatson In my defense, I did actually ask the same question, just also added an 'or...' :) Is there a proper SO etiquette for this? Should I edit either the question or body? – Robba Feb 04 '16 at 13:42
  • 4
    Actually, it is not the `await` keyword but actually the `async` keyword that causes the compiler to create a state machine. On MSDN, in [this article](https://msdn.microsoft.com/en-us/magazine/hh456402.aspx), see `SimpleBodyAsync()` and the description of its IL. – DavidRR May 31 '16 at 16:01
  • 1
    Edited in regards to @DavidRR's comment. Thanks for the correction! – CodingGorilla Jul 19 '16 at 14:08
2

You would return a task to avoid creating a state machine. But as it turns out it seems recommended to prefere async task over just task.

https://github.com/davidfowl/AspNetCoreDiagnosticScenarios/blob/master/AsyncGuidance.md#prefer-asyncawait-over-directly-returning-task

Wouter
  • 2,540
  • 19
  • 31