I’ve figured out how to do it without async/await
or TaskCompletionSource
, using nested tasks and Task.Unwrap
instead.
First, to address @mikez’s comment, here’s GetResponseAsync
implementation for .NET 4.0:
static public Task<WebResponse> GetResponseTapAsync(this WebRequest request)
{
return Task.Factory.FromAsync(
(asyncCallback, state) =>
request.BeginGetResponse(asyncCallback, state),
(asyncResult) =>
request.EndGetResponse(asyncResult), null);
}
Now, here’s GetResponseWithRetryAsync
implementation:
static Task<HttpWebResponse> GetResponseWithRetryAsync(string url, int retries)
{
if (retries < 0)
throw new ArgumentOutOfRangeException();
var request = WebRequest.Create(url);
Func<Task<WebResponse>, Task<HttpWebResponse>> proceedToNextStep = null;
Func<Task<HttpWebResponse>> doStep = () =>
{
return request.GetResponseTapAsync().ContinueWith(proceedToNextStep).Unwrap();
};
proceedToNextStep = (prevTask) =>
{
if (prevTask.IsCanceled)
throw new TaskCanceledException();
if (prevTask.IsFaulted && --retries > 0)
return doStep();
// throw if failed or return the result
return Task.FromResult((HttpWebResponse)prevTask.Result);
};
return doStep();
}
It’s been an interesting exercise. It works, but I think its the way more difficult to follow, than the async/await
version.