Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HttpClient To Get List With Async/Await Operation

I'm trying to get a list from a web api which I've written before. Then I'll use that list in Xamarin.Forms ListView. My code is here:

public static class DataSource
{

    public static async Task<Restoran[]> GetRestoransAsync()
    {
        // ... Use HttpClient.
        using (HttpClient client = new HttpClient())
        using (HttpResponseMessage response = await client.GetAsync(page))
        using (HttpContent content = response.Content)
        {
            // ... Read the string.
            string result = await content.ReadAsStringAsync();

            var restorans = JsonConvert.DeserializeObject<Restoran[]>(result);
            return restorans;
        }

    }

}

My ContentPage:

public class MenuPage : ContentPage
{
    ListView listView;
    List<Restoran> restorans = new List<Restoran>();
    async Task LoadRestorans()
    {
        restorans = (await DataSource.GetRestoransAsync()).ToList();
    }

    public MenuPage(string masa)
    {
        var loadData = LoadRestorans();
        loadData.Wait();
        listView = new ListView(ListViewCachingStrategy.RecycleElement)
        {
        ItemsSource = restorans,
            ItemTemplate = new DataTemplate(() => {
                var nativeCell = new CustomCell();
                return nativeCell;
            })
        };
    }
}

But when I debugged this code, "LoadRestorans()" method has called just before initialization of "restorans" list. I think I don't understand the mentality of async methods. What should I do?

like image 599
emrea Avatar asked Mar 05 '26 02:03

emrea


1 Answers

You have two options.

Create the page using an async factory method

public class MenuPage : ContentPage {
    ListView listView;
    List<Restoran> restorans = new List<Restoran>();

    private async Task LoadRestoransAsync() {
        restorans = (await DataSource.GetRestoransAsync()).ToList();
        listView = new ListView(ListViewCachingStrategy.RecycleElement) {
            ItemsSource = restorans,
            ItemTemplate = new DataTemplate(() => {
                var nativeCell = new CustomCell();
                return nativeCell;
            })
        };
    }

    public MenuPage(string masa) {
        //...        
    }

    public static async Task<MenuPage> CreateMenuPageAsync(string masa) {
        var page = new MenuPage(masa);
        await page.LoadRestoransAsync();
        return pagel
    }
}

Then use it like this in other async event handlers

var page = await MenuPage.CreateMenuPageAsync("<masa here>");

OR

do it in the OnAppearing event.

Subscribe to the Appearing event of the page/view

protected override void OnAppearing() {
    this.Appearing += Page_Appearing;
}

and call your async code on an actual even handler

private async void Page_Appearing(object sender, EventArgs e) {
    //...call async code here

    //unsubscribing from the event
    this.Appearing -= Page_Appearing;
}

The full class would look something like this

public class MenuPage : ContentPage {
    ListView listView;
    List<Restoran> restorans = new List<Restoran>();

    private async Task LoadRestoransAsync() {
        restorans = (await DataSource.GetRestoransAsync()).ToList();
        listView = new ListView(ListViewCachingStrategy.RecycleElement) {
            ItemsSource = restorans,
            ItemTemplate = new DataTemplate(() => {
                var nativeCell = new CustomCell();
                return nativeCell;
            })
        };
    }

    public MenuPage(string masa) {
        //...        
    }

    protected override void OnAppearing() {
        this.Appearing += Page_Appearing;
    }

    private async void Page_Appearing(object sender, EventArgs e) {
        //...call async code here
        await LoadRestoransAsync();

        //unsubscribing from the event
        this.Appearing -= Page_Appearing;
    }

}

What was happening before was that by calling .Wait(), which is a blocking call the class was mixing async and blocking calls (like .Result and .Wait()) which can lead to deadlocks. Hence why you were unable to go past the method when you tried to test it.

like image 82
Nkosi Avatar answered Mar 06 '26 17:03

Nkosi



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!