gpt4 book ai didi

c# - 将异步/等待转换为 Task.ContinueWith

转载 作者:太空狗 更新时间:2023-10-29 21:39:28 27 4
gpt4 key购买 nike

这个问题是由对 this one 的评论触发的:

如何在没有 Microsoft.Bcl.Async 的情况下将非线性 async/await 代码反向移植到 .NET 4.0?

在链接的问题中,我们有一个 WebRequest 操作,如果它一直失败,我们希望重试有限的次数。 Async/await 代码可能如下所示:

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,就像这样(未经测试):

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 的情况下执行此操作?

最佳答案

我已经想出了如何在没有 async/awaitTaskCompletionSource 的情况下,使用嵌套任务和 Task.Unwrap 来做到这一点。

首先,为了解决@mikez 的评论,这里是 .NET 4.0 的 GetResponseAsync 实现:

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();
}

这是一个有趣的练习。它有效,但我认为它比 async/await 版本更难遵循。

关于c# - 将异步/等待转换为 Task.ContinueWith,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21345673/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com