Calling an async method in a constructor

Using lazy initialization to call an async method from a constructor

Home DailyDrop

Daily Knowledge Drop

As it is not possible to await an async method in a constructor, if an async method has to be called, a valid technique is to call the async method in the constructor, but defer the await until later.


Non async

Calling a non async method in a constructor is straight forward:

public class MyClass
{
	private readonly string _specialData;

    public MyClass()
    {
    	// takes long, not ideal
    	_specialData = ExternalService.GetData();
    }
}

Here, an external service is called to get data required for MyClass to function correctly.

However, is the scenario when ExternalService.GetData() is an async method ExternalService.GetDataAsync() handled?


Async

GetResult

If the only option to get the require data is via a async method, simply just calling it in the constructor will not work:

public class MyClass
{
	private readonly string _specialData;


    public MyClass()
    {
        // Won't work!
        // Return type is Task<string> not string
        // _specialData = ExternalService.GetDataAsync();

        // Won't work!
        // To use await, the method needs to be async
        // but constructors cannot be async
        //_specialData = await ExternalService.GetDataAsync();
    }
    	
}

One not recommended option is to do the following:

public class MyClass
{
    private readonly string _specialDataTask;

    public MyClass()
    {
    	_specialData = ExternalService.GetDataAsync().GetAwaiter().GetResult();
    }
}

.GetAwaiter().GetResult() is used on the Task returned from GetDataAsync - generally not a good idea to use this approach.


Lazy

A better option is to use the lazy initialization approach:

public class MyClass
{
    private readonly Task<string> _specialDataTask;

    public MyClass()
    {
       _specialDataTask = ExternalService.GetDataAsync();
    }

    public async Task DoWorkUsingSpecialDataAsync()
    {
       var _specialData = await _specialDataTask.ConfigureAwait(false);

       // Do the work using _specialData
    }
}
  • The type of the "_specialData" variable was changed from string to Task<string>
  • In the constructor, the async method is called, but not awaited - this returns a Task<string>
  • When/if the "_specialData" value is required, then the Task<string> is awaited to get the actual value

With this method, when the constructor is called, the process of getting the special data is initiated, but is not blocking. When it comes time to use the value returned from the process, await is used to get the value from the Task. Either the processing is finished and the value will be available immediately, or the process is still ongoing and the value will be returned once the process is completed.


Notes

Ideally, this scenario should probably be avoided all together - but if it is a requirement, the lazy-initialization technique is great way to solve the problem.


References

Lazy and once-only C# async initialization

Daily Drop 243: 26-01-2023

At the start of 2022 I set myself the goal of learning one new coding related piece of knowledge a day.
It could be anything - some.NET / C# functionality I wasn't aware of, a design practice, a cool new coding technique, or just something I find interesting. It could be something I knew at one point but had forgotten, or something completely new, which I may or may never actually use.

The Daily Drop is a record of these pieces of knowledge - writing about and summarizing them helps re-enforce the information for myself, as well as potentially helps others learn something new as well.
c# .net async constructor