Working with Asynchronous Methods in C#

In yesterday’s article, “Motivation for async/await in C#“, we have seen why asynchronous programming is important. We have also seen basic usage of the await keyword, which requires its containing method to be marked as async.

When learning to write asynchronous methods, it is not trivial to get the interactions between various methods (which may or may not be asynchronous) right. In fact, the examples in yesterday’s article which use an async void method should normally only be used in event handlers, and even so, there are caveats to consider.

In this article, we’ll go through various different scenarios in which async/await can be used.

async Task methods

Let’s take another look at the asynchronous (event handler) method from yesterday’s article:

        private async void Button_Click(object sender, RoutedEventArgs e)
        {
            var baseAddress = new Uri("http://mta.com.mt");

            using (var httpClient = new HttpClient() { BaseAddress = baseAddress })
            {
                var response = await httpClient.GetAsync("/");
                var content = await response.Content.ReadAsStringAsync();

                MessageBox.Show("Response arrived!", "Slow website");
            }
        }

Try moving out the code into a separate method, and awaiting it from the event handler. You’ll find that you can’t await an async void method:

In order to be able to await a method, it must return Task (if it doesn’t need to return anything, such as void methods) or Task<T> (if it needs to return a value of type T). We also append an –Async suffix to the method name by convention to make it obvious for people who use such methods that they’re meant to be awaited.

Thus, this example becomes:

        private async void Button_Click(object sender, RoutedEventArgs e)
        {
            await GetHtmlAsync();
        }

        private async Task GetHtmlAsync()
        {
            var baseAddress = new Uri("http://mta.com.mt");

            using (var httpClient = new HttpClient() { BaseAddress = baseAddress })
            {
                var response = await httpClient.GetAsync("/");
                var content = await response.Content.ReadAsStringAsync();

                MessageBox.Show("Response arrived!", "Slow website");
            }
        }

This is an example of an async Task method, which does not return anything. Let’s change it such that it returns the HTML from the response:

        private async void Button_Click(object sender, RoutedEventArgs e)
        {
            string html = await GetHtmlAsync();
        }

        private async Task<string> GetHtmlAsync()
        {
            var baseAddress = new Uri("http://mta.com.mt");

            using (var httpClient = new HttpClient() { BaseAddress = baseAddress })
            {
                var response = await httpClient.GetAsync("/");
                var content = await response.Content.ReadAsStringAsync();

                return content;
            }
        }

We’ve changed the signature of GetHtmlAsync() to return Task<string> intead of just Task. Correspondingly, we are now returning content (a string) from the method. At the event handler, we are now assigning the result of the await into the html variable. Apart from waiting asynchronously until the method completes, await has the additional function of unwrapping the result from the Task that contains it; thus html is of type string.

If you try removing the async keyword from GetHtmlAsync(), you’ll learn a little more about the actual function of the async keyword:

Without async, you are expected to return what the method advertises: a Task<string>. On the other hand, if you mark the method as async, the meaning of the method is changed such that you can return a string directly. The underlying Task-related plumbing is handled by the compiler.

Chaining Asynchronous Methods

In the previous section, we have seen how methods need to return a Task in order to be awaited. Typically, one async Task method will call another and await its result.

The chain of calls ends at a last async Task, typically provided by the .NET Framework or other library, which interfaces directly with I/O (e.g. network or filesystem). It must be an asynchronous method; attempting to disguise a blocking call as async here will lead to deadlocks.

async Task may (and should) be used all the way from an incoming request to the final I/O library method in application types that support top-level asynchronous methods, such as Web API or WCF.

The situation is a little different for other applications (e.g. Windows Forms, WPF) that are event-driven. Asynchronous event handlers are a special case where we need to use async void methods, as we have already seen in the WPF example from yesterday’s article:

async void methods

Event handlers are void methods that are called by the runtime in a dispatcher loop. Thus, async void methods are necessary to allow usage of await within event handlers without requiring them to return a Task

However, as we have seen before, there is no way to await an async void method. This makes async void methods very dangerous to use outside of their intended context, as I have detailed in “The Dangers of async void Event Handlers“. This is one of the more common mistakes when programming with async/await, and it is good to become familiar with the problems in order to avoid repeating the same mistakes in future.

Fake Asynchronous Methods

Sometimes, you’ll have an interface that requires asynchronous methods, yet your implementation does not need anything asynchronous in it. Let’s look at a practical example:

    public interface ISimpleStorage
    {
        Task WriteAsync(string str);
        Task<string> ReadAsync();
    }

You could implement this interface using a simple file as a backing store, in which case your methods will be suitably asynchronous:

    public class FileStorage : ISimpleStorage
    {
        public async Task<string> ReadAsync()
        {
            using (var fs = File.OpenRead("file.txt"))
            using (var sr = new StreamReader(fs))
            {
                var str = await sr.ReadToEndAsync();
                return str;
            }
        }

        public async Task WriteAsync(string str)
        {
            using (var fs = File.OpenWrite("file.txt"))
            using (var sw = new StreamWriter(fs))
            {
                await sw.WriteAsync(str);
                await sw.FlushAsync();
            }
        }
    }

However, you could have another implementation which just uses memory as storage, and in this case there’s nothing asynchronous:

    public class MemoryStorage : ISimpleStorage
    {
        private string str;

        public Task<string> ReadAsync()
        {
            return Task.FromResult(str);
        }

        public Task WriteAsync(string str)
        {
            this.str = str;
            return Task.CompletedTask;
        }
    }

In that case, your methods need not be marked async. However, this means that you will actually need to return Task instances from each method. If you have nothing to return, then just return a Task.CompletedTask (available from .NET Framework 4.6 onwards). You can also use Task.FromResult() to construct a task from a variable that you want to return.

Simplifying Single Line Asynchronous Methods

Consider the following asynchronous method:

        static async Task WaitALittleAsync()
        {
            await Task.Delay(10000);
        }

Here, we are waiting for the delay to finish, and then returning the result.

Instead, we can just return the Task itself, and let the caller do the awaiting:

        static Task WaitALittleAsync()
        {
            return Task.Delay(10000);
        }

Once again, a method does not need to be marked async if (a) it does not await anything, and (b) it can return a Task, rather than some other type that needs to be wrapped in a Task.

Using async/await in Main()

Until recently, you couldn’t use async/await in a console application’s Main() method. You can have an async Task Main() method as from C# 7.1, but you need to make sure you’re using C# 7.1 first from your project properties -> Build -> Advanced…:

You can choose either “C# 7.1”, or “C# latest minor version (latest)”. With that, you can await directly from within Main():

        static async Task Main(string[] args)
        {
            await WaitALittleAsync();
        }

Before C# 7.1, you had to use some other workaround to await from Main(). One option is to use a special AsyncContext such as the one written by Stephen Cleary. Another is to just move asynchrony out of Main() and use a Console.ReadLine() to keep the window open:

        static void Main(string[] args)
        {
            Run();
            Console.ReadLine();
        }

        static async void Run()
        {
            await WaitALittleAsync();
        }

Summary

  1. Use async Task methods wherever you can.
  2. Use async void methods for event handlers, but beware the dangers even there.
  3. Use the –Async suffix in asynchronous method names to make them easily recognisable as awaitable.
  4. Use Task.CompletedTask and Task.FromResult() to satisfy asynchronous contracts with synchronous code.
  5. Asynchronous code in Main() has not been possible until recently. Use C# 7.1+ or a workaround to get this working.

5 thoughts on “Working with Asynchronous Methods in C#”

  1. Hi. Very nice article on async/await. I didn’t know that Main could be asynchronous in C# 7.1! I just wanted to point out that there’s a better way to run console apps asynchronously in earlier versions of C# utilizing the Wait() method.

    class Program
    {
    static void Main(string[] args)
    {
    MainAsync(args).Wait();
    }

    static async Task MainAsync(string[] args)
    {
    await Task.Delay(TimeSpan.FromSeconds(10));
    }
    }

    This is one of the few places that the Wait() method won’t cause a deadlock.

    Happy coding!

Leave a Reply

Your email address will not be published. Required fields are marked *