转换异步/等待到Task.ContinueWith [英] Converting async/await to Task.ContinueWith
问题描述
此问题是由评论这个触发:
如何备份端口非线性异步/计谋
code到.NET 4.0没有 Microsoft.Bcl.Async
?
How to back-port a non-linear async/await
code to .NET 4.0 without Microsoft.Bcl.Async
?
在链接的问题,我们有一个的WebRequest
运行,我们要重新尝试的次数有限,如果它不断失败。该异步/计谋
code可能是这样的:
In the linked question, we have a WebRequest
operation we want to retry for a limited number of times, if it keeps failing. The Async/await
code could look like this:
async Task<HttpWebResponse> GetResponseWithRetryAsync(string url, int retries)
{
if (retries < 0)
throw new ArgumentOutOfRangeException();
var request = WebRequest.Create(url);
while (true)
{
WebResponse task = null;
try
{
task = request.GetResponseAsync();
return (HttpWebResponse)await task;
}
catch (Exception ex)
{
if (task.IsCanceled)
throw;
if (--retries == 0)
throw; // rethrow last error
// otherwise, log the error and retry
Debug.Print("Retrying after error: " + ex.Message);
}
}
}
这是第一个想到的,我会使用 TaskCompletionSource
,因为像这样(未经测试):
From the first thought, I'd use TaskCompletionSource
, as something like this (untested):
Task<HttpWebResponse> GetResponseWithRetryAsync(string url, int retries)
{
if (retries < 0)
throw new ArgumentOutOfRangeException();
var request = WebRequest.Create(url);
var tcs = new TaskCompletionSource<HttpWebResponse>();
Action<Task<WebResponse>> proceesToNextStep = null;
Action doStep = () =>
request.GetResponseAsync().ContinueWith(proceedToNextStep);
proceedToNextStep = (prevTask) =>
{
if (prevTask.IsCanceled)
tcs.SetCanceled();
else if (!prevTask.IsFaulted)
tcs.SetResult((HttpWebResponse)prevTask.Result);
else if (--retries == 0)
tcs.SetException(prevTask.Exception);
else
doStep();
};
doStep();
return tcs.Task;
}
现在的问题是,如何做到这一点,而不 TaskCompletionSource
?
The question is, how to do this without TaskCompletionSource
?
推荐答案
我已经找到了如何做到这一点,而不异步/计谋
或 TaskCompletionSource
,使用嵌套任务和 Task.Unwrap
代替。
I've figured out how to do it without async/await
or TaskCompletionSource
, using nested tasks and Task.Unwrap
instead.
首先,为解决@ mikez的评论,这里的 GetResponseAsync
实施.NET 4.0:
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);
}
现在,这里的 GetResponseWithRetryAsync
实施
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();
}
这是一个有趣的练习。它的工作原理,但我认为它的方式更加难走,比异步/计谋
版本。
这篇关于转换异步/等待到Task.ContinueWith的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!