Coder Perfect

In the constructor, call an asynchronous method?

Problem

In a constructor, I’d like to invoke an asynchronous method. Is that even possible?

Details: I have a getwritings() method that parses JSON data. If I call getwritings() in an async method and put await to the left of it, everything works well. When I try to populate a LongListView on my page, however, getWritings() returns null, and the LongListView is empty.

To solve the problem, I changed the return type of getWritings() to TaskListWriting>> and then used getWritings() to retrieve the result in the constructor (). Result. However, this causes the UI thread to be blocked.

public partial class Page2 : PhoneApplicationPage
{
    List<Writing> writings;

    public Page2()
    {
        InitializeComponent();
        getWritings();
    }

    private async void getWritings()
    {
        string jsonData = await JsonDataManager.GetJsonAsync("1");
        JObject obj = JObject.Parse(jsonData);
        JArray array = (JArray)obj["posts"];

        for (int i = 0; i < array.Count; i++)
        {
            Writing writing = new Writing();
            writing.content = JsonDataManager.JsonParse(array, i, "content");
            writing.date = JsonDataManager.JsonParse(array, i, "date");
            writing.image = JsonDataManager.JsonParse(array, i, "url");
            writing.summary = JsonDataManager.JsonParse(array, i, "excerpt");
            writing.title = JsonDataManager.JsonParse(array, i, "title");

            writings.Add(writing);
        }

        myLongList.ItemsSource = writings;
    }
}

Asked by Kaan Baris Bayrak

Solution #1

The ideal way is to recognize and design for the asynchronous nature of the download.

To put it another way, decide how your application will look while the data is being downloaded. Allow the page constructor to create that view, and then begin the download. When the download is finished, refresh the page to see the results.

You might find a blog post about asynchronous constructors handy. There are also two MSDN articles on asynchronous data binding (if you’re using MVVM) and asynchronous best practices (if you’re not using MVVM) (i.e., you should avoid async void).

Answered by Stephen Cleary

Solution #2

You can also do something similar to this:

Task.Run(() => this.FunctionAsync()).Wait();

Take cautious not to suffocate your threads!

Answered by Peter Stegnar

Solution #3

I’d want to share a pattern that I’ve been employing to tackle similar issues. It appears to operate quite well, in my opinion. Of course, this only works if you have command over the constructor’s caller. Here’s an example:

public class MyClass
{
    public static async Task<MyClass> Create()
    {
        var myClass = new MyClass();
        await myClass.Initialize();
        return myClass;
    }

    private MyClass()
    {

    }

    private async Task Initialize()
    {
        await Task.Delay(1000); // Do whatever asynchronous work you need to do
    }
}

We essentially make the constructor private and create our own public static async function to create an instance of MyClass. We ensured that no one could “accidentally” make an instance of this class without running the necessary initialization methods by making the constructor private and maintaining the static method within the same class. The class still contains all of the logic behind the object’s creation (just within a static method).

var myClass1 = new MyClass() // Cannot be done, the constructor is private
var myClass2 = MyClass.Create() // Returns a Task that promises an instance of MyClass once it's finished
var myClass3 = await MyClass.Create() // asynchronously creates and initializes an instance of MyClass

It would look like this if it were implemented in the current scenario:

public partial class Page2 : PhoneApplicationPage
{
    public static async Task<Page2> Create()
    {
        var page = new Page2();
        await page.getWritings();
        return page;
    }

    List<Writing> writings;

    private Page2()
    {
        InitializeComponent();
    }

    private async Task getWritings()
    {
        string jsonData = await JsonDataManager.GetJsonAsync("1");
        JObject obj = JObject.Parse(jsonData);
        JArray array = (JArray)obj["posts"];

        for (int i = 0; i < array.Count; i++)
        {
            Writing writing = new Writing();
            writing.content = JsonDataManager.JsonParse(array, i, "content");
            writing.date = JsonDataManager.JsonParse(array, i, "date");
            writing.image = JsonDataManager.JsonParse(array, i, "url");
            writing.summary = JsonDataManager.JsonParse(array, i, "excerpt");
            writing.title = JsonDataManager.JsonParse(array, i, "title");

            writings.Add(writing);
        }

        myLongList.ItemsSource = writings;
    }
}

Rather than acting,

var page = new Page2();

You’d be involved in

var page = await Page2.Create();

Answered by Shazi

Solution #4

Creating an action and running it asynchronously is a quick approach to do a time-consuming activity in any constructor.

new Action( async() => await InitializeThingsAsync())();

This code will not cause your UI to become unresponsive or leave any loose threads. And, if you need to update any UI (assuming you’re not using the MVVM technique), you may do so with the Dispatcher, as many people have advised.

Note: If you don’t have any init, onload, or navigated overrides, this option will only allow you to start an execution of a method from the constructor. This will most likely continue to operate even after the construction has been done. As a result, the constructor may not have access to the result of this function call.

Answered by Anup Sharma

Solution #5

Substitute:

myLongList.ItemsSource = writings;

with this

Dispatcher.BeginInvoke(() => myLongList.ItemsSource = writings);

Answered by csharpwinphonexaml

Post is based on https://stackoverflow.com/questions/23048285/call-asynchronous-method-in-constructor