仅当每个任务成功完成/验证时才链接基于任务的方法

Chaining task-based methods only if each task completes / validates successfully

我有一个任务返回方法链,所有返回一些 Task<SomeResponse<T>>SomeResponse<T> 是一个通用响应 class 公开属性,例如响应是否成功 (IsSuccess),如果成功则包含返回对象的 T Data 属性,以及如果不是随附的错误消息。

假设我有 3 个这样的方法(它们都返回 SomeResponse<T>)。我只想继续一个接一个地执行任务,直到其中一个失败或所有任务都成功。流程如下所示:

var first = await firtTask(someParam);
if (!first.IsSuccess) return first;
var second = await secondTask(first.Data);
if (!second.IsSuccess) return second;
var third = await thirdTask(second.Data);
return third; // doesn't matter if it succeeded or not as it's the last one, no need to check.

我这里的问题是每个调用的SomeResponse<T>在进行下一个等待之前需要验证是否成功,这增加了很多重复的验证代码。检查每个任务是否成功完成是不够的,因为我必须在继续下一个任务之前检查它的 SomeResponse<T>.IsSuccess 属性。

我尝试在 Task<SomeResponse<T>> 之上为此创建一个扩展方法:

public static Task<SomeResponse<T>> OnSuccessChainAsync<T>(this Task<SomeResponse<T>> startingTask, Func<T, Task<SomeResponse<T>>> continuationTask)
{
    // omitting null checks etc

    var continuation = startingTask.ContinueWith(
        async previousTask =>
        {
            var response = await previousTask.ConfigureAwait(false);
            if (!response.IsSuccess)
            {
                return response;
            }

            return await continuationTask(response.Data).ConfigureAwait(false);
        }, TaskScheduler.Current);

    return continuation.Unwrap();
}

现在我可以写:

public override Task<SomeResponse<TValue>> AddAsync(TValue someValue)
{
    return firstTask(someValue)
           .OnSuccessChainAsync(secondTask)
           .OnSuccessChainAsync(thirdTask);
}

我不确定我是否走错了方向。我将 async-await 与 TPL 的 ContinueWith 混合在一起,最重要的是我从我的分析器中得到了 VSTHRD003 Avoid awaiting foreign Tasks

不要将旧式 ContinueWithasync/await 混用。 事实上,请尝试完全避免 ContinueWith:它非常复杂,有很多微妙的行为,其中一半你不想去想,而另一半则用 async/await 表达得更清楚。

为什么不简化一下:

public static async Task<SomeResponse<T>> ExecuteInSequence<T>(
    T firstData,
    params Func<T, Task<Response<T>>>[] funcs)
{
    T data = firstData;
    foreach (var func in funcs)
    {
        var response = await func(data);
        if (!response.IsSuccess)
        {
            return response;
        }

        data = response.Data;
    }

    return data;
}

那你可以这样写:

ExecuteInSequence(someValue, task1, task2, task3);

没有任何混合,没有链接,只有一个简单的循环。


如果您要将此作为 Task<SomeResponse<T>> 上的扩展方法编写,我仍会将所有内容保留为 awaits:

public static async Task<SomeResponse<T>> OnSuccessChainAsync<T>(
    this Task<SomeResponse<T>> startingTask,
    Func<T, Task<SomeResponse<T>>> continuationTask)
{
    // startingTask will probably have already completed (especially if
    // it's one which we created on a previous invocation), in which case
    // this await will be synchronous.

    var result = await startingTask;
    if (!result.IsSuccess)
    {
        return result;
    }

    return await continuationTask(result.Data);
}