转换异步/等待到Task.ContinueWith [英] Converting async/await to Task.ContinueWith

查看:294
本文介绍了转换异步/等待到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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆