Async tips and tricks

The async and await keywords along with the Task and Task<T> classes provide a great way to do asynchronous work in C#. However, there might be some confusion when to mark a method as async, when to await a task and when to use the Task class. In this post I will try to clear some things up using some simple examples.

Avoid async void

It’s tempting to change a regular void method to async void. You should, however, use async Task for these methods and avoid async void since exceptions that occur inside an async void method are handled different than when you await a Task and it will crash the process. The only exception here is for events.

Bad:

public async void DoWorkAsync()
{
    // ...
}

Good:

public async Task DoWorkAsync()
{
    // ...
}

Reference: Haacked.com, MSDN

Return a Task when you can

When an async method calls another async method. You can either choose to await the task and return the result or return the task to be awaited by the caller. It’s better to return the task to the caller. This avoids unnecessary wrapping of methods in a state machine.

In stead of:

public async Task<Result> HandleAsync(SomeQuery query)
{
    return await _dispatcher.DispatchQuery(query);
}

public async Task HandleAsync(SomeCommand command)
{
    await _dispatcher.DispatchCommand(command);
}

Do this:

public Task<Result> HandleAsync(SomeQuery query)
{
    return _dispatcher.DispatchQuery(query);
}

public Task HandleAsync(SomeCommand command)
{
    return _dispatcher.DispatchCommand(command);
}

Do return await when you have multiple awaits

The exception for the above is when you have some async intermediate method which you await, you should await when returning too:

public async Task<Result> HandleAsync(SomeQuery query)
{
    var intermediate = await _repository.GetAsync(query);
    return await _dispatcher.DispatchQuery(intermediate);
}

Leaving out the await keyword even causes your code not compile. If you use await anywhere, you have to await everything.

Use Task.WhenAll() for multiple tasks

When you have multiple tasks which are independent of eachother, you could either await each task or use Task.WhenAll(). It’s prefered to use Task.WhenAll() since that only creates one state machine.

In stead of:

public async Task RunAsyncAwaitEach()
{
    var t1 = DoWorkAsync1();
    var t2 = DoWorkAsync2();
    var t3 = DoWorkAsync3();

    await t1;
    await t2;
    await t3;
}

Use this:

public async Task RunAsyncWhenAll()
{
    await Task.WhenAll(DoWorkAsync1(), DoWorkAsync2(), DoWorkAsync3());
}

When one of the tasks returns a value, you can use the Result property of Task<T>:

var someTask = DoWorkAsync();
var someTaskWithResult = GetResultAsync();

await Task.WhenAll(someTask, someTaskWithResult);

var result = someTaskWithResult.Result;

Reference: StackOverflow

Written on November 27, 2015