【发布时间】:2021-07-21 22:30:58
【问题描述】:
我有一个任务返回方法链,都返回一些Task<SomeResponse<T>>。 SomeResponse<T> 是一个通用响应类,它公开属性,例如响应是否成功 (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。
【问题讨论】:
-
在我看来你重新发明了异常,但更糟。在这里使用异常将完全符合您的要求:在它们发生时停止执行,并且不可能忘记检查是否发生过
-
@canton7 每个返回
SomeResponse<T>的基于任务的方法都在调用一个可能会在那里抛出异常的外部项目。SomeResponse<T>通知另一端发生了一些事情(通过IsSuccess)而没有抛出异常(这一端没有出错)。 -
我知道事情是这样的 -- 我是说如果你改变 以便你抛出一个异常而不是使用
Response<T>.IsSuccess,您将不需要所有这些杂乱、容易出错的开销。例外很有用,有自己的位置,并且很好地集成到了语言中。 -
嗯,在这里想了解一下:如果我调用了一个
GetCustomerByName(canton7)API,而API 本身返回了一个404等,我为什么要在我身边抛出一个Exception? -
因为它提供了你想要的行为,即停止执行你当前正在执行的方法,并返回给调用者。它有一个很好的属性,你不能忘记检查 API 是否返回 404。
标签: c# async-await task-parallel-library