WebRequest.GetResponseAsync (with timeout) for PCL

I've been working on porting Budgie over to the Portable Class Library framework so it'll be usable from WinRT apps as well as "desktop" .NET apps.

Budgie's TwitterClient class has a Timeout property which applies to all requests, but the PCL version of WebRequest has such property, since it has no synchronous GetResponse method that would honour it.

So here's a simple extension method which uses the BeginGetResponse and EndGetResponse methods, and wraps them up in a task with a timeout.

internal static class WebRequestExtensions
{
    internal static Task<WebResponse> GetResponseAsync(this WebRequest request, TimeSpan timeout)
    {
        return Task.Factory.StartNew<WebResponse>(() =>
        {
            var t = Task.Factory.FromAsync<WebResponse>(
                request.BeginGetResponse,
                request.EndGetResponse,
                null);

            if (!t.Wait(timeout)) throw new TimeoutException();

            return t.Result;
        });
    }
}

This method uses the FromAsync<T> factory method to spin up a new task from the old "begin/end" asynchronous method pairs, but does so inside a task so it can wait for the result with a timeout without blocking. If the timeout expires, it simply throws a TimeoutException.

c# .net pcl
Posted by: Matt Hamilton
Last revised: 28 Mar, 2024 11:27 AM History

Trackbacks

Comments

OmariO
OmariO
10 Nov, 2012 03:04 PM

Matt, your solution is asynchronous i.e. not blocking the thread calling GetResponseAsync, but it synchronous in relation to the thread pool thread on which task.Wait is called. So the extension would be useful in UI (to unload waiting to another thread) but not for server side, where fully nonblocking wating is a requirement for scalability (in terms of thread usage).

10 Nov, 2012 11:28 PM

Good point, Omar.

The ideal way to do this is to spin up two tasks, one which does the HTTP call and the other which simply uses Task.Delay to sleep, then use Task.WhenAny to wait for one of them to finish.

The problem with that approach is that the Portable Class Library framework subset doesn't include Task.Delay or Task.WhenAny.

@JakeGinnivan has suggested taking a dependency on Microsoft.Bcl.Async, which will make these methods available via a TaskEx class, but I'm torn as I want to limit Budgie's dependencies. It's a conundrum!

No new comments are allowed on this post.