none
WebClient.DownloadStringAsync - why is this not "async"? RRS feed

  • Question

  • I was surprised to discover that this method cannot be used with the new async/await pattern.

    On Windows Phone 8 (for example) there are no synchronous versions of this method, so I must use this Async method - but this method returns void.

    Being forced to setup an event handler and so on is a bit disappointing when so much other code on Phone and Store use async/await.

    Is this just something that MS had no time to address or is there some good reason why the async/await pattern cannot be used on WebClient?

    Cap'n



    Friday, March 1, 2013 10:02 PM

Answers

  • Yeah - you can always wrap an EAP style async method into a Task<T>, if you need to do that -

    It's not ideal (I'd prefer using the original), but you can add this to WP8 by making an extension method in your project:

    public static Task<string> DownloadStringTaskAsync(this WebClient client, Uri uri)
    {
        var tcs = new TaskCompletionSource<string>();
    
        client.DownloadStringCompleted += (o,e) =>
        {
            if (e.Exception != null)
               tcs.SetException(e.Exception);
            else if (e.Cancelled)
               tcs.SetCancelled();
            else
               tcs.SetResult(e.Result);
        };
    
        client.DownloadStringAsync(uri);
        return tcs.Task;
    }
    
    

    This will let you use the API the same way you do in the full framework.


    Reed Copsey, Jr. - http://reedcopsey.com
    If a post answers your question, please click "Mark As Answer" on that post and "Mark as Helpful".

    Monday, March 4, 2013 4:55 PM
    Moderator
  • The problem is DownloadStringAsync existed before Task, so it uses EAP. In this cases, you'll need to use the new DownloadStringTaskAsync overload: http://msdn.microsoft.com/en-us/library/hh138332.aspx

    These will work with the async/await language support.


    Reed Copsey, Jr. - http://reedcopsey.com
    If a post answers your question, please click "Mark As Answer" on that post and "Mark as Helpful".

    Friday, March 1, 2013 11:12 PM
    Moderator

All replies

  • The problem is DownloadStringAsync existed before Task, so it uses EAP. In this cases, you'll need to use the new DownloadStringTaskAsync overload: http://msdn.microsoft.com/en-us/library/hh138332.aspx

    These will work with the async/await language support.


    Reed Copsey, Jr. - http://reedcopsey.com
    If a post answers your question, please click "Mark As Answer" on that post and "Mark as Helpful".

    Friday, March 1, 2013 11:12 PM
    Moderator
  • The problem is DownloadStringAsync existed before Task, so it uses EAP. In this cases, you'll need to use the new DownloadStringTaskAsync overload: http://msdn.microsoft.com/en-us/library/hh138332.aspx

    These will work with the async/await language support.


    Reed Copsey, Jr. - http://reedcopsey.com
    If a post answers your question, please click "Mark As Answer" on that post and "Mark as Helpful".

    Thanks Reed - but sadly that overload is absent when running on Phone 8 - write once run anywhere - is being challenged here!

    Cap'n

    Monday, March 4, 2013 4:35 PM
  • Yeah - you can always wrap an EAP style async method into a Task<T>, if you need to do that -

    It's not ideal (I'd prefer using the original), but you can add this to WP8 by making an extension method in your project:

    public static Task<string> DownloadStringTaskAsync(this WebClient client, Uri uri)
    {
        var tcs = new TaskCompletionSource<string>();
    
        client.DownloadStringCompleted += (o,e) =>
        {
            if (e.Exception != null)
               tcs.SetException(e.Exception);
            else if (e.Cancelled)
               tcs.SetCancelled();
            else
               tcs.SetResult(e.Result);
        };
    
        client.DownloadStringAsync(uri);
        return tcs.Task;
    }
    
    

    This will let you use the API the same way you do in the full framework.


    Reed Copsey, Jr. - http://reedcopsey.com
    If a post answers your question, please click "Mark As Answer" on that post and "Mark as Helpful".

    Monday, March 4, 2013 4:55 PM
    Moderator
  • Yeah - you can always wrap an EAP style async method into a Task<T>, if you need to do that -

    It's not ideal (I'd prefer using the original), but you can add this to WP8 by making an extension method in your project:

    public static Task<string> DownloadStringTaskAsync(this WebClient client, Uri uri)
    {
        var tcs = new TaskCompletionSource<string>();
    
        client.DownloadStringCompleted += (o,e) =>
        {
            if (e.Exception != null)
               tcs.SetException(e.Exception);
            else if (e.Cancelled)
               tcs.SetCancelled();
            else
               tcs.SetResult(e.Result);
        };
    
        client.DownloadStringAsync(uri);
        return tcs.Task;
    }
    

    This will let you use the API the same way you do in the full framework.


    Reed Copsey, Jr. - http://reedcopsey.com
    If a post answers your question, please click "Mark As Answer" on that post and "Mark as Helpful".

    Good point - I've taken that step and it looks like the way to go - seems fine!

    Thanks

    Cap'n

    Tuesday, March 5, 2013 12:09 PM
  • Hi Reed,

    Thanks for this post! Just a couple of questions I hope you'll be able to help me with!

    1. What do you mean by your prefer the original? why isn't it ideal to use an extension?

    2. When I use the suggested extension, I just hangs

    Any ideas why?

                ThreadPool.QueueUserWorkItem(async =>
                {
                    var dispatcher = Deployment.Current.Dispatcher;
                    dispatcher.BeginInvoke(() =>
                    {
                        IsLoading = true;
                        try
                        {
                            wc.DownloadStringAsync(new Uri(@"https://<ip>/webservices/documentdeliveryservice.svc/json/rentals"));
    
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine(ex.Message);
                        }
                    });
    Note that I'm pointing a public ip which has an invalid cert error and while I figured out how to handle this via a windows form app, I haven't yet with wp8. The thing I don't get is that if I just call WebClient directly, the

    wc_DownloadStringCompleted is raised and I can see the error, but it doesn't just hang.

    Any ideas?

    Thanks.




    • Edited by tfierens Sunday, May 12, 2013 11:11 PM
    Sunday, May 12, 2013 11:09 PM
  • tfierens - yyou're not using the extension (it was DownloadStringTaskAsync) - also, why are you queuing something, then using Dispatcher.BeginInvoke (which puts it back on the UI thread) in one operation?

    Reed Copsey, Jr. - http://reedcopsey.com
    If a post answers your question, please click "Mark As Answer" on that post and "Mark as Helpful".

    Monday, May 13, 2013 3:55 PM
    Moderator
  • Currently if you build Reed's code in Portable class library, the extension should look like this due to e.Exception being e.Error and also Cancelled in SetCancelled for some reason having only one l with Canceled (I don't make this up):

    public static Task<string> DownloadStringTaskAsync(this WebClient client, Uri uri)
    {
        var tcs = new TaskCompletionSource<string>();
    
        client.DownloadStringCompleted += (o, e) =>
        {
            if (e.Error != null)
                tcs.SetException(e.Error);
            else if (e.Cancelled)
                tcs.SetCanceled();
            else
                tcs.SetResult(e.Result);
        };
    
        client.DownloadStringAsync(uri);
        return tcs.Task;
    }


    William Wegerson (www.OmegaCoder.Com)


    Monday, May 20, 2013 6:16 PM
    Moderator
  • This won't work with Silverlight.
    Thursday, November 19, 2015 8:31 PM