cubesnyc
cubesnyc

Reputation: 1545

async httpclient operation

I am trying to design a browser that will fetch site updates programmatically. I am trying to do this with async/await methods but when I try and run the program it seems to just hang on response.Wait();. not sure why or whats happening.

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        var urls = sourceUrls();
        Task<HttpResponseMessage> response = login(urls[0]);
        response.Wait();

        Console.Write( "Complete" );

    }

    private List<Site> sourceUrls()
    {

        var urls = new List<Site>();
        urls.Add(new Site("http://yahoo.com", "test", "test"));

        return urls;
    }
}

browser class::

static public class Browser
{

    private static CookieContainer cc = new CookieContainer();
    private static HttpClientHandler handler = new HttpClientHandler();
    public static HttpClient browser = new HttpClient(handler);

    static Browser()
    {
        handler.CookieContainer = cc;
    }


    static public async Task<HttpResponseMessage> login(Site site)
    {
        var _postData = new Dictionary<string, string>
        {
            {"test", "test"}
        };

        FormUrlEncodedContent postData = new FormUrlEncodedContent(_postData);
        HttpResponseMessage response = await browser.PostAsync(site.url, postData);
        return response;
    }
}

Also, for my intended purposes, is it ok to make browser a static function, or does that not make sense? Sorry for the questions, new to c#

Upvotes: 5

Views: 812

Answers (3)

NeddySpaghetti
NeddySpaghetti

Reputation: 13495

It's a bad idea to call Wait on a Task in a UI thread, it results in a deadlock. You can simply await the response which will achieve what you want. The await unwraps the response from the Task so your return type is now just HttpResponseMessage. Because you can't mark constructors as async you can move the functionality to another method that kicks off the operation.

public MainWindow()
{
    InitializeComponent();
    LoadUrlsAsync();
}

public async Task LoadUrlsAsync()
{
    var urls = sourceUrls();
    HttpResponseMessage response = await login(urls[0]);

    Console.Write( "Complete" );
}

See this article for best practices when using async/await.

Alternatively you can use the Task.ConfigureAwait extension method to configure the continuation not to run on the current SyncrhronizationContext which should also avoid the deadlock.

public MainWindow()
{
    InitializeComponent();

    var urls = sourceUrls();
    Task<HttpResponseMessage> response = login(urls[0]).ConfigureAwait(false);
    response.Wait();

    Console.Write( "Complete" );
}

Upvotes: 1

Stephen Cleary
Stephen Cleary

Reputation: 456477

Wait causes a deadlock that I explain in full on my blog. The best solution is to use await as Ned suggested.

In your case, constructors cannot be async, so that's not possible. This should be an indication that what you're trying to do may not be the best thing to do. In particular, you're trying to block the UI thread waiting for a download to complete; this is almost never a good idea.

Instead, have your constructor start the asynchronous operation and (synchronously) load a view that shows no data - e.g., a "loading" screen or an empty view, whatever makes the most sense for your problem domain. Then, when the asynchronous operation completes, it should update the view with the new data. I have an article on async data binding that explores one possible technique.

Upvotes: 1

Colby Cavin
Colby Cavin

Reputation: 492

Both GetUrl() and GetUrl2(), below, accomplish what you're looking to do in slightly different ways. Be aware, however, that Task.Wait will block the current thread (in this case, the UI thread) until the task completes. I'm guessing this isn't what you want to do.

using System.Diagnostics;
using System.Net.Http;
using System.Threading.Tasks;
using System.Windows;

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        GetUrls();
        GetUrls2();
    }
    private void GetUrls()
    {
        var response = Task.Run(() => Browser.login("http://yahoo.com"));
        response.Wait();
        Debug.WriteLine("Complete GetUrls()");
    }
    private void GetUrls2()
    {
        var browseTask = Browser.login("http://yahoo.com");
        browseTask.ContinueWith((r) => Debug.WriteLine("Complete GetUrls2()"));
    }
}
static public class Browser
{
    static public async Task<HttpResponseMessage> login(string site)
    {
        var browser = new HttpClient();
        return await browser.GetAsync(site);
    }
}

Upvotes: 0

Related Questions