I have a bunch of slow functions that are essentially this:
private async Task<List<string>> DownloadSomething()
{
var request = System.Net.WebRequest.Create("https://valid.url");
...
using (var ss = await request.GetRequestStreamAsync())
{
await ss.WriteAsync(...);
}
using (var rr = await request.GetResponseAsync())
using (var ss = rr.GetResponseStream())
{
//read stream and return data
}
}
This works nicely and asynchronously except for the call to WebRequest.Create
- this single line freezes the UI thread for several seconds which sort of ruins the purpose of async/await.
I already have this code written using BackgroundWorker
s, which works perfectly and never freezes the UI.
Still, what is the correct, idiomatic way to create a web request with respect to async/await? Or maybe there is another class that should be used?
I've seen this nice answer about asyncifying a WebRequest
, but even there the object itself is created synchronously.
Interestingly, I'm not seeing a blocking delay with
WebRequest.Create
orHttpClient.PostAsync
. It might be something to do with DNS resolution or proxy configuration, although I'd expect these operations to be implemented internally as asynchronous, too.Anyway, as a workaround you can start the request on a pool thread, although this is not something I'd normally do:
That would keep the UI responsive, but it might be difficult to cancel it if user wants to stop the operation. That's because you need to already have a
WebRequest
instance to be able to callAbort
on it.Using
HttpClient
, cancellation would be possible, something like this:With
HttpClient
, you can also register ahttpClient.CancelPendingRequests()
callback on the cancellation token, like this.[UPDATE] Based on the comments: in your original case (before introducing
Task.Run
) you probably did not need theIProgress<I>
pattern. As long asDownloadSomething()
was called on the UI thread, every execution step after eachawait
insideDownloadSomething
would be resumed on the same UI thread, so you could just update the UI directly in betweenawaits
.Now, to run the whole
DownloadSomething()
viaTask.Run
on a pool thread, you would have to pass an instance ofIProgress<I>
into it, e.g.:Note, because
DownloadSomething
is anasync
method itself, it is now run as a nested task, whichTask.Run
transparently unwraps for you. More on this: Task.Run vs Task.Factory.StartNew.Also check out: Enabling Progress and Cancellation in Async APIs.
I think you need to use HttpClient.GetAsync() which returns a task from an HTTP request.
http://msdn.microsoft.com/en-us/library/hh158912(v=vs.110).aspx
It may depend a bit on what you want to return, but the HttpClient has a whole bunch of async methods for requests.