Async Task VS Async Void

async/await - when to return a Task vs void?

  1. Normally, you would want to return a Task. The main exception should be when you need to have a void return type (for events). If there's no reason to disallow having the caller await your task, why disallow it?

  2. async methods that return void are special in another aspect: they represent top-level async operations, and have additional rules that come into play when your task returns an exception. The easiest way is to show the difference is with an example:

static async void f()
{
await h();
}

static async Task g()
{
await h();
}

static async Task h()
{
throw new NotImplementedException();
}

private void button1_Click(object sender, EventArgs e)
{
f();
}

private void button2_Click(object sender, EventArgs e)
{
g();
}

private void button3_Click(object sender, EventArgs e)
{
GC.Collect();
}

f's exception is always "observed". An exception that leaves a top-level asynchronous method is simply treated like any other unhandled exception. g's exception is never observed. When the garbage collector comes to clean up the task, it sees that the task resulted in an exception, and nobody handled the exception. When that happens, the TaskScheduler.UnobservedTaskException handler runs. You should never let this happen. To use your example,

public static async void AsyncMethod2(int num)
{
await Task.Factory.StartNew(() => Thread.Sleep(num));
}

Yes, use async and await here, they make sure your method still works correctly if an exception is thrown.

For more information see: https://learn.microsoft.com/en-us/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming

async Task vs async void

When you call an async void method, or call an async Task method without awaiting it (if the called method contains an await, so it doesn't block), your code will continue right away, without waiting for the method to actually complete. This means that several invocations of the method can be executing in parallel, but you won't know when they actually complete, which you usually need to know.

You can take advantage of executing in parallel like this, while also being able to wait for all the invocations to complete by storing the Tasks in a collection and then using await Task.WhenAll(tasks);.

Also keep in mind that if you want to execute code in parallel, you have to make sure it's safe to do it. This is commonly called "thread-safety".

Task.Run vs async void | Task.Run vs Async Sub

Firstly: do not use async void. I realize that it expresses the semantics of what you want, but there are some framework internals that actively explode if they encounter it (it is a long, uninteresting story), so: don't get into that practice.

Let's pretend that we have:

private async Task DoSomething() {...}

in both cases, for that reason.


The main difference here is that from the caller's perspective there is no guarantee that DoSomething won't run synchronously. So in the case:

public async task MainThread() {
_ = DoSomething(); // note use of discard here, because we're not awaiting it
}

DoSomething will run on the main thread at least as far as the first await - specifically, the first incomplete await. The good news is: you can just add:

await Task.Yield();

as the first line in DoSomething() and it is guaranteed to return immediately to the caller (because Task.Yield is always incomplete, essentially), avoiding having to go via Task.Run. Internally, Task.Yield() does something very similar to Task.Run(), but it can skip a few unnecessary pieces.

Putting that all together - if it was me, I would have:

public async Task MainThread() {
_ = DoSomething();

// Continue with other stuff and don't care about DoSomething()
}
private async Task DoSomething() {
await Task.Yield();

// Doing long running stuff
}

Blazor UI Update Async void vs Async Task

[Polite]You just need to get your head around asynchronous programming. What's happening is that the UI rendering completes before your asynchronous task. The void routine DoLogin awaits the completion of the API call, but the methods up the call stack don't, there's no Task to await. When the API call yields control, it gets handed back up the calling stack. DoLogin waits for the Task completion before completing the rest of the code in the method, but yields to the calling method which then carries on past the call to DoLogin- in your case rendering your control.

The async Void Method pattern is fire and forget, you get no Task reference to wait on. async Task Method means you can await Method in the calling routine. You can read more here in one of my Code Project articles.

What's the difference between returning void and returning a Task?

SLaks and Killercam's answers are good; I thought I'd just add a bit more context.

Your first question is essentially about what methods can be marked async.

A method marked as async can return void, Task or Task<T>. What are the differences between them?

A Task<T> returning async method can be awaited, and when the task completes it will proffer up a T.

A Task returning async method can be awaited, and when the task completes, the continuation of the task is scheduled to run.

A void returning async method cannot be awaited; it is a "fire and forget" method. It does work asynchronously, and you have no way of telling when it is done. This is more than a little bit weird; as SLaks says, normally you would only do that when making an asynchronous event handler. The event fires, the handler executes; no one is going to "await" the task returned by the event handler because event handlers do not return tasks, and even if they did, what code would use the Task for something? It's usually not user code that transfers control to the handler in the first place.

Your second question, in a comment, is essentially about what can be awaited:

What kinds of methods can be awaited? Can a void-returning method be awaited?

No, a void-returning method cannot be awaited. The compiler translates await M() into a call to M().GetAwaiter(), where GetAwaiter might be an instance method or an extension method. The value awaited has to be one for which you can get an awaiter; clearly a void-returning method does not produce a value from which you can get an awaiter.

Task-returning methods can produce awaitable values. We anticipate that third parties will want to create their own implementations of Task-like objects that can be awaited, and you will be able to await them. However, you will not be allowed to declare async methods that return anything but void, Task or Task<T>.

(UPDATE: My last sentence there may be falsified by a future version of C#; there is a proposal to allow return types other than task types for async methods.)

(UPDATE: The feature mentioned above made it in to C# 7.)

What are the differences between `async void` (with no await) vs `void`

If there is no current synchronization context (as in my example), where is the exception raised?

By convention, when SynchronizationContext.Current is null, that's really the same as SynchronizationContext.Current equal to an instance of new SynchronizationContext(). In other words, "no synchronization context" is the same as the "thread pool synchronization context".

So, the behavior you're seeing is that the async state machine is catching the exception and then raising it directly on a thread pool thread, where it cannot be caught with catch.

This behavior seems odd, but think about it this way: async void is intended for event handlers. So consider a UI application raising an event; if it is synchronous, then any exceptions get propagated to the UI message processing loop. The async void behavior is intended to mimic that: any exceptions (including ones after await) are re-raised on the UI message processing loop. This same logic is applied to the thread pool context; e.g., exceptions from your synchronous System.Threading.Timer callback handler will be raised directly on the thread pool, and so will exceptions from your asynchronous System.Threading.Timer callback handler.

If it runs synchronously with no await, why is it behaving differently from simply void?

The async state machine is handling the exceptions specially.

Does async Task with no await also behave differently from Task?

Absolutely. async Task has a very similar state machine - it catches any exceptions from your code and places them on the returned Task. This is one of the pitfalls in eliding async/await for non-trivial code.

What are the differences in compiler behaviour between async void and async Task.

For the compiler, the difference is just how exceptions are handled.

The proper way to think about this is that async Task is a natural and appropriate language development; async void is a weird hack that the C#/VB team adopted to enable asynchronous events without huge backwards-compatibility issues. Other async/await-enabled languages such as F#, Python, and JavaScript have no concept of async void... and thus avoid all of its pitfalls.

Is a Task object really created under-the-hood for async void as suggested here?

No.



Related Topics



Leave a reply



Submit