1

I can't seem to get async to work the way I want. I'm pretty unfamiliar with it in general, but my research has led me to it. Here's my situation: I have a XAML form I'm loading that needs data from a web service. I can get the data, no problem. The issue is that I want the form to load with a "Please wait. Data loading..." screen that I have made, but nothing loads until the query finishes which can take up to 10 seconds, which is a poor user experience. Can anyone let me know what I'm doing wrong?

In my ViewModel main section, I call LoadData() to get the ball rolling.

I also had a thought...is async what I really want here?

    public async void LoadData()
    {
        IsLoading = Visibility.Visible;
        MonitorData downloadInfo = await GetWebApiInfo();
        try
        {
            AssignDataToControls(downloadInfo);
            IsLoading = Visibility.Collapsed;
            Console.WriteLine("Loaded successfully.");
        }
        catch (Exception e)
        {
            IsLoading = Visibility.Collapsed;
            Console.WriteLine("Error: " + e);
        }
    }

    private void AssignDataToControls(MonitorData mon)
    {
        MainPanel.Clear();
        mon.MainPanel.ToList().ForEach(x => MainPanel.Add(x));
        Information = mon.Information;
        MonitorText = mon.MonitorText;
        ProgressData = mon.progList;
    }

    public async Task<MonitorData> GetWebApiInfo()
    {
        main = new MonitorData();

        var url = "::::::::WEB API ADDRESS::::::::";
        var request = (HttpWebRequest)WebRequest.Create(url);
        //Task<HttpWebRequest> req = await (HttpWebRequest)WebRequest.Create(url);
        //HttpWebRequest request = await GetWebRequest(url);
        WebResponse response = request.GetResponse();
        Stream dataStream = response.GetResponseStream();
        StreamReader reader = new StreamReader(dataStream, Encoding.Unicode);
        string responseFromServer = reader.ReadToEnd();
        var deserializer = new JavaScriptSerializer();
        main = deserializer.Deserialize<MonitorData>(responseFromServer);

        reader.Dispose();
        response.Dispose();
        return main;
    }
3
  • You need a BackgroundWorker, so the web call is handled in another thread. The problem with async methods in your case is that you still need to wait before returning the control to the UI thus blocking the UI thread. Commented Nov 21, 2016 at 21:05
  • It seems that you may be looking for some way to paginate the data into smaller portions so that you can display those "pages" as they're complete. That sounds more like asynchrony in the client than in a controller or service. Async methods in the back-end won't hurt, but they won't solve the problem. Commented Nov 21, 2016 at 21:05
  • From whatever method you are calling the LoadData method, you need to put the call to IsLoading = Visibility.Visible; their after it right after the call to LoadData. Await returns control to the calling function until the awaited command finishes Commented Nov 21, 2016 at 21:33

1 Answer 1

3

The compiler will tell you exactly what's wrong with that approach. Specifically, your GetWebApiInfo method is going to run synchronously because you never use await.

In this case, you can use HttpClient to download the information asynchronously. It's a lot easier than mucking around with WebResponse and stuff:

private static readonly HttpClient _client = new HttpClient();
public async Task<MonitorData> GetWebApiInfo()
{
  var url = "::::::::WEB API ADDRESS::::::::";
  string responseFromServer;
  using (var dataStream = await _client.GetStreamAsync())
  using (var reader = new StreamReader(dataStream, Encoding.Unicode))
    responseFromServer = await reader.ReadToEndAsync();
  var deserializer = new JavaScriptSerializer();
  return deserializer.Deserialize<MonitorData>(responseFromServer);
}

In general, when you want to "make something async", you should start from the lowest-level APIs. E.g., don't start by marking GetWebApiInfo with async; start with the actual HTTP transfer and call it with await. Then let async/await grow from there.

You may find my NotifyTask<T> type helpful; it allows you to do things like show/hide busy indicators with data binding.

Sign up to request clarification or add additional context in comments.

1 Comment

I meant to comment when you posted this answer, but I got sidetracked with other projects. Thanks so much, Stephen! This is a great solution. I had to tinker with how it's being called to get everything working, but it does work just fine now. Thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.