仅当每个任务成功完成/验证时才链接基于任务的方法
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。
不要将旧式 ContinueWith
与 async/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>>
上的扩展方法编写,我仍会将所有内容保留为 await
s:
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);
}
我有一个任务返回方法链,所有返回一些 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。
不要将旧式 ContinueWith
与 async/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>>
上的扩展方法编写,我仍会将所有内容保留为 await
s:
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);
}