Daniel
Daniel

Reputation: 1037

Async Online Search freezing WPF-UI

I'm pretty new to threading in C#. I've got a Searchfield which triggers HTTP-Request against an API on every Key pressed. After the Request the Searchresult is displayed in a ListView in my SearchTab. Of course this freezes the UI for a few milliseconds until the HTTP-Request is finished. So I tried to make the Request Async.

private async void textBoxSearch_KeyUp(object sender, KeyEventArgs e)
{

    SearchTab searchTab;

    Task<SearchContainer<SearchMovie>> searchTask = searchMovie(textBoxSearch.Text);

    if (searchTabExists())
    {
        searchTab = getSearchTab();
    }
    else
    {
        searchTab = new SearchTab();
        mainTabControl.Items.Insert(mainTabControl.Items.Count, searchTab);
    }
    searchTab.IsSelected = true;

    SearchContainer<SearchMovie> results = await searchTask;
    searchTab.updateSearch(results);
}

async Task<SearchContainer<SearchMovie>> searchMovie(String query)
{
    var today = await Task.FromResult<SearchContainer<SearchMovie>>(tmdbClient.SearchMovie(query, "de"));
    return today;
}

This Code makes it a bit better but it still freezes the UI a bit, because at some point it has to wait for the API-Call. I want to be able to type my search smoothly without interruption and display the result if the API-Call is finished in time before another search is made (KeyPressed).

How do you solve such a problem in C#? In JAVA I would use a AsyncWorker which calls the Displaymethod after the search is finished (or cancel the worker if another search is made) so that searching and displaying isn't done in the Main-Thread.

Is there a similar construct in C#? I can only find async await solutions if I search for multithreading. Or can is use it in a way that it works like I wish.

Upvotes: 1

Views: 604

Answers (1)

Gary McLean Hall
Gary McLean Hall

Reputation: 994

Task.FromResult creates an already-successful task given a result. In your example, the result comes from the call to tmdbClient.SearchMovie(). I assume this call is blocking.

This code is not truly asynchronous because you are still waiting on a blocking call. You need an asynchronous version of the tmdbClient.SearchMovie() method. Often, these are obvious because they are suffixed with Async. So, you need a method like tmdbClient.SearchMovieAsync().

If no such method is provided by your client library, then you will need to convert a blocking call to an asynchronous call.

One simple, but flawed and problematic, way of doing this is to use Task.Run:

(This is untested pseudocode)

Task<SearchContainer<SearchMovie>> searchMovie(String query)
{
    return Task.Run(() => tmdbClient.SearchMovie(query, "de"));
}

The problem here is that you are using a thread to do a network IO call, which is a bad use of resources: the background thread is waiting on the result of the blocking call and cannot be used for anything until the blocking call completes. It should give you a more responsive UI, but with the same thread resource cost as threading yet the complexity of async.

Upvotes: 6

Related Questions