Coder Perfect

In C#, what is the meaning of “return await”?


Is there any circumstance in which a writing method such as this:

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

instead of this:

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

would make sense?

Why use the return await construct when the inner DoAnotherThingAsync() call may return TaskT> directly?

I’ve seen return await code in so many places that I worry I’ve missed something. However, I believe that in this scenario, not using the async/await keywords and simply returning the Task would be functionally equal. Why would you want to add an extra await layer to your code?

Asked by TX_

Solution #1

Return in a normal method and return await in an async method work differently in one tricky case: when paired with using (or, more generally, any return await in a try block).

Consider the following two methods:

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

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

When the DoAnotherThingAsync() function returns, which is probable before it completes, the first method will Dispose() the Foo object. This indicates that the first version is likely to be buggy (due to Foo’s premature disposal), whereas the second version will function properly.

Answered by svick

Solution #2

If you don’t need async (i.e., you can just return the Task), don’t use it.

Return await is beneficial in various instances, such as when doing two asynchronous operations:

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

See Stephen Toub’s MSDN article and video on async performance for further information.

Update: I’ve prepared a blog article with a lot more information.

Answered by Stephen Cleary

Solution #3

The only time you’d want to do it is if there’s another await in the previous code, or if you’re modifying the result in some way before returning it. Another way this could happen is if you use a try/catch statement that modifies how exceptions are handled. If you’re not doing any of those things, then you’re correct; there’s no need to add the overhead of making the method async.

Answered by Servy

Solution #4

Another instance in which you may need to wait for the outcome is this:

async Task<IFoo> GetIFooAsync()
    return await GetFooAsync();

async Task<Foo> GetFooAsync()
    var foo = await CreateFooAsync();
    await foo.InitializeAsync();
    return foo;

Because the type of T differs between the two methods and TaskFoo is not immediately assignable to TaskIFoo>, GetIFooAsync() must wait for the return of GetFooAsync(). However, if you wait for the result, it just becomes Foo, which can be assigned to IFoo. The async method then just repackages the result inside TaskIFoo> and you’re done.

Answered by Andrew Arnott

Solution #5

You risk destroying your stack trace during debugging or when it’s reported in the logs on exceptions if you don’t use return await.

When you return the task, the method has completed its task and has been removed from the call stack. You’re leaving it in the call stack when you use return await.

For example:

When using await, call stack: A is waiting for a task from B, while B is waiting for a task from C.

When not using await, call stack: A is waiting for the work from C, which B has already completed.

Answered by haimb

Post is based on