Where do I call an async method from?

348 views Asked by At

I'm having a bit of trouble calling an async method. The method is in a class and loads a text file. I want to load the text file when the application starts. However, I cannot call an async method from a constructor. Most examples I have looked at online often call methods from async button press events.

Currently, I am trying this from my constructor:

Task.Run(async () =>
{
    this.categories = await GenerateCategories(numberOfCategories);
});

Although this works, the rest of the program continues after calling this method and code that uses "categories" executes causing a crash (since categories is still null because the task isn't complete yet).

So in summary, where and how is it best to call this method from? I don't want it to load when the user presses a button or anything and I'd like "categories" to be populated before any more code executes.

I am using C# and writing a universal app so loading the file has to be async.

2

There are 2 answers

0
antlersoft On

The simple answer is that Task.Run() returns a task. If you call the .Wait() method on the task, your thread will pause until the task is complete and the categories member is set.

This ignores, however, that the method was probably created async for a reason. You probably don't want to tie up your UI thread Wait()'ing for an async method to complete. You might also want to pay attention to this note on the manual page for Task.Run:

The Run(Func) method is used by language compilers to support the async and await keywords. It is not intended to be called directly from user code.

Instead, you can create an async void method to call from the constructor that will run the task asynchronously in a background thread. You should make sure that all the logic that depends on GenerateCategories having run is run from that method.

To use async methods properly, you have to decouple their flow of control from the main user interface thread. You can send a message from within the task to the UI using Control.Invoke when the task needs to inform the UI of its progress.

0
Stephen Cleary On

I have a series on my blog about using async in "impossible" situations; one post covers async constructors in particular.

Since you're writing a universal app, the important thing to keep in mind is that the user interface must initially display immediately (synchronously); doing asynchronous work before your initial view displays is simply not an option. Instead, you should (synchronously) initialize/construct into a "loading" state, and then update your UI when the file is loaded.

I have a three-part series of MSDN articles on asynchronous patterns for MVVM applications, which you may find helpful.