What Is the Purpose of "Return Await" in C#

How and when to use ‘async’ and ‘await’

When using async and await the compiler generates a state machine in the background.

Here's an example on which I hope I can explain some of the high-level details that are going on:

public async Task MyMethodAsync()
{
Task<int> longRunningTask = LongRunningOperationAsync();
// independent work which doesn't need the result of LongRunningOperationAsync can be done here

//and now we call await on the task
int result = await longRunningTask;
//use the result
Console.WriteLine(result);
}

public async Task<int> LongRunningOperationAsync() // assume we return an int from this long running operation
{
await Task.Delay(1000); // 1 second delay
return 1;
}

OK, so what happens here:

  1. Task<int> longRunningTask = LongRunningOperationAsync(); starts executing LongRunningOperation

  2. Independent work is done on let's assume the Main Thread (Thread ID = 1) then await longRunningTask is reached.

    Now, if the longRunningTask hasn't finished and it is still running, MyMethodAsync() will return to its calling method, thus the main thread doesn't get blocked. When the longRunningTask is done then a thread from the ThreadPool (can be any thread) will return to MyMethodAsync() in its previous context and continue execution (in this case printing the result to the console).

A second case would be that the longRunningTask has already finished its execution and the result is available. When reaching the await longRunningTask we already have the result so the code will continue executing on the very same thread. (in this case printing result to console). Of course this is not the case for the above example, where there's a Task.Delay(1000) involved.

How should we use async await?

Every-time you call await it creates a lump of code to bundle up variables, captures the synchronization context (if applicable) and create a continuation into an IAsyncStateMachine.

Essentially, returning a Task without the async keyword will give you a small run-time efficiency and save you a bunch of CIL. Do note that the Async feature in .NET also has many optimizations already. Also note (and importantly) that returning a Task in a using statement will likely throw an Already Disposed Exception.

You can compare the CIL and plumbing differences here

  • Forwarded Task
  • Async Method

So if your method is just forwarding a Task and not wanting anything from it, you could easily just drop the async keyword and return the Task directly.

More-so, there are times when we do more than just forwarding and there is branching involved. This is where, Task.FromResult and Task.CompletedTask come into play to help deal with the logic of what may arise in a method. I.e If you want to give a result (there and then), or return a Task that is completed (respectively).

Lastly, the Async and Await Pattern has subtle differences when dealing with Exceptions. If you are returning a Task, you can use Task.FromException<T> to pop any exception on the the returned Task like an async method would normally do.

Nonsensical example

public Task<int> DoSomethingAsync(int someValue)
{
try
{
if (someValue == 1)
return Task.FromResult(3); // Return a completed task

return MyAsyncMethod(); // Return a task
}
catch (Exception e)
{
return Task.FromException<int>(e); // Place exception on the task
}
}

In short, if you don't quite understand what is going on, just await it; the overhead will be minimal. However, if you understand the subtitles of how to return a task result, a completed task, placing an exception on a task, or just forwarding. You can save your self some CIL and give your code a small performance gain by dropping the async keyword returning a task directly and bypassing the IAsyncStateMachine.


At about this time, I would look up the Stack Overflow user and author Stephen Cleary, and Mr. Parallel Stephen Toub. They have a plethora of blogs and books dedicated solely to the Async and Await Pattern, all the pitfalls, coding etiquette and lots more information you will surely find interesting.

At the end of an async method, should I return or await?

You can't return the task if the method itself is declared to be async - so this won't work, for example:

async Task BarAsync()
{
return BazAsync(); // Invalid!
}

That would require a return type of Task<Task>.

If your method is just doing a small amount of work and then calling just one async method, then your first option is fine, and means there's one fewer task involved. You should be aware that any exceptions thrown within your synchronous method will be delivered synchronously though - indeed, this is how I prefer to handle argument validation.

It's also a common pattern for implementing overloading e.g. by cancellation token.

Just be aware that if you need to change to await something else, you'll need to make it an async method instead. For example:

// Version 1:
Task BarAsync()
{
// No need to gronkle yet...
return BazAsync();
}

// Oops, for version 2 I need to do some more work...
async Task BarAsync()
{
int gronkle = await GronkleAsync();
// Do something with gronkle

// Now we have to await BazAsync as we're now in an async method
await BazAsync();
}

Where is the return statement in async/await

The async keyword transforms the method and constructs the returned Task instance. There is nothing returned from the async void method because it returns void; this lack of a Task is one reason why you should avoid async void. async void is not a natural asynchronous method signature; it is only supported so that event handlers may be async.

If you want to return a value, then you should have the method return a Task<T>, e.g., Task<int> BlahAsync(), and then you can just return the value directly, e.g., return 13; The number of awaits in the method has nothing to do with it. When the method executes the actual return (e.g., return 13), the async keyword interprets that as completing the Task<int> that was already constructed.

I have an async intro on my blog that you may find helpful.

Is it useful to mark a method async when it only awaits at the return statement?

doesn't this mean that method is basically synchronous

No. It's asynchronous. You're probably thinking of sequential (progressing from one thing to the next), not synchronous (blocking the current thread). An await will pause the method (sequentially) but not block the thread (asynchronously). For more information, see my async intro.

without the async modifier

While you could elide the async/await keywords, I would recommend that you do not. This is because // some more code here may throw an exception. I cover this and other considerations in my blog post on eliding async and await.

and the suffix Async?

No, that suffix is appropriate for any method that returns an awaitable (e.g., Task). So, even if you elide the async and await, it's still returning a task that should be awaited, so it should still have the Async suffix.

You can think of it this way: the Async suffix is part of the API interface. The async keyword is an implementation detail. They often go together, but not always.

Why does this async function expect a return value

You return a bool value while your method actually returns a Task<bool>.

Change the method to return the awaited Task.

return await Task.Run(...);

async Task then await Task vs Task then return task

It is almost the same (in terms of threads etc.). But for the second one (using await) a lot more overhead will be created by the compiler.

Methods declared as async and using await are converted into a state machine by the compiler. So when you hit the await, the control flow is returned to the calling method and execution of your async method is resumed after the await when the awaited Task has finished.

As there is no more code after your await, there is no need to use await anyway. Simply return the Task is enough.



Related Topics



Leave a reply



Submit