Skip to content
Samuel Tambunan's Blog
Go back

Asyncifying Brownfield Code

Async/await

Async/await is one of those language features that once you start using, will grow in your code base as any calling method to an async method, will need to be itself asynchronous. Once started, it grows and grows until almost all the methods are asynchronous methods, which is a pretty good thing.

One downside of this is when you’re migrating brownfield code to use async/await. There are a couple of patterns that can be used to help with this.

Create a duplicate asynchronous method

Let’s say you have a method PrintName() that is a synchronous method.

public void PrintName() { 
    // Do something synchornouse
    Writer.PrintLine("My name is Sam");
}

In order to start converting your code base to be asynchronous, you can create a secondary method called PrintNameAsync() that will contain the same logic as PrintName() but be asynchornous.

public async Task PrintNameAsync() { 
    // Do something asynchronous
    await Writer.PrintLineAsync("My name is Sam");
}

The problem with this approach is that when you update the logic for PrintName(), you’ll need to update the logic for PrintNameAsync() as well. This creates duplicate code and can be quite confusing when the method itself becomes really large.

The other pattern that I favour more is one I read from a Microsoft blog post that uses a private method and a parameter to specify sync or async.

Using a private method and an async parameter

Instead of having two methods that contains the two logic, a single private method is created that accepts a parameter to decide if the method should run synchronously or asynchronously.


public async Task PrintNameAsync() { 
    await PrintNameCoreAsync(true);
}

public void PrintName() {
    // Since this is a synchronous call, it is fine to use GetAwaiter().GetResult()
    PrintNameCoreAsync(false).GetAwaiter().GetResult();
}

private async Task PrintNameCoreAsync(bool async = false) { 
    // Do something else first

    if (async) { 
        // Do something asynchronous
        await Writer.PrintLineAsync("My name is Sam");
    } else { 
        // Syncrhonous calls
        Writer.PrintLine("My name is Sam");
    }
}

By having an async parameter that decides whether the method can run synchronously or not, the logic to run the method is contained in one place. This can reduce duplicate code and calling GetAwaiter().GetResult() will be fine since the synchronous code path will be used. The synchronous code path will always return a completed task will can then just be awaited and unwrapped.


Share this post:

Previous Post
Using a default home endpoint
Next Post
Builder pattern