我正在将一些异步/等待代码转换为链式任务,因此我可以在已发布的框架中使用它。等待代码如下所示
public async Task<TraumMessage> Get() {
var message = await Invoke("GET");
var memorized = await message.Memorize();
return memorized;
}
,其中
Task<TraumMessage> Invoke(string verb) {}
Task<TraumMessage> Memorize() {}
我希望链接Invoke
和Memorize
以返回Memorize
生成的任务,但这会产生Task<Task<TraumMessage>
。我最终得到的解决方案是TaskCompletionSource<TraumMessage>
作为我的信号:
public Task<TraumMessage> Get() {
var completion = new TaskCompletionSource<TraumMessage>();
Invoke("GET").ContinueWith( t1 => {
if(t1.IsFaulted) {
completion.SetException(t1.Exception);
return;
}
t1.Result.Memorize().ContinueWith( t2 => {
if(t2.IsFaulted) {
completion.SetException(t2.Exception);
return;
}
completion.SetResult(t2.Result);
});
});
return completion.Task;
}
有没有办法在没有TaskCompletionSource
的情况下完成此任务?
答案 0 :(得分:4)
是的,该框架附带了一个方便的Unwrap()扩展方法,可以满足您的需求。
Invoke("GET").ContinueWith( t => t.Result.Memorize() ).Unwrap();
如果您正在取消,那么您显然需要将取消令牌传递到适当的位置。
答案 1 :(得分:1)
我认为这几乎是实现目标的唯一途径。连续API不支持将不同的任务链接在一起,因此您必须使用TaskCompletionSource
来协调工作。
我没有在这台机器上安装Async CTP,但为什么不用反编译器(或ILDASM,如果你知道如何读取IL)查看代码,看看它在做什么。我敢打赌,它与你的TCS代码非常相似。
答案 2 :(得分:0)
您可以使用附加的子任务。只有当所有子任务完成后,父任务才会转换为已完成状态。异常将传播到父任务。 您将需要一个结果持有者,因为结果将在父任务的委托完成后分配,但将在父任务继续运行时设置。
像这样:
public class Holder<T> where T: class
{
public T Value { get; set; }
}
public Task<Holder<TraumMessage>> Get() {
var invokeTask = Invoke("GET");
var result = invokeTask.ContinueWith<Holder<TraumMessage>>(t1 => {
var holder = new Holder<TraumMessage>();
var memorizeTask = t1.Result.Memorize();
memorizeTask.ContinueWith(t2 => {
holder.Value = t2.Result;
}, TaskContinuationOptions.AttachedToParent);
return holder;
});
return result;
}