我使用HttpClient的,我想写类似的东西:
HttpClient client = ...;
Task<string> getContent()
{
return client.GetAsync(...)
.ContinueWith( t => t.Result.Content.ReadAsStringAsync() );
}
我知道我可以写
.ContinueWith( t => t.Result.Content.ReadAsStringAsync().Result);
但池的一个线程将被阻止。我想continueWithTask像Google Task library。
我怎样才能做到呢?
UPDATE
是的,我确实需要使用,而不是异步任务/等待,我真的知道我想要什么。
UPDATE2
我修改了我的看法,现在我觉得我错在选择技术。如果有人怀疑,这里是不错的代码great example。
这些天,你应该避免ContinueWith
,宁愿async
/ await
除非你有非常非常具体的原因;我怀疑这将工作:
async Task<string> getContent()
{
var foo = await client.GetAsync(...); // possibly with .ConfigureAwait(false)
return await foo.Content.ReadAsStringAsync(); // possibly with .ConfigureAwait(false)
}
是的,我确实需要使用,而不是异步任务/等待,我真的知道我想要什么。
我强烈建议Marc's answer。除非你是卡在.NET 4.0(即Windows XP中)我想不出一个很好的理由不使用async
/ await
。而且由于你使用HttpClient
和Task.Run
不能如此。所以,请记住,这个答案是纯粹的教学,不推荐用于生产。
ContinueWith
调用可以“链接”,善良,类似于Promise.then
如何工作在JavaScript中,但超出的即装即用C#链接语义不是JavaScript的更加尴尬。
一方面,Task<Task<T>>
类型不会自动解开。有可用的Unwrap
方法。另外,使用.Result
- 一个TPL遗物与ContinueWith
更自然的使用 - 将包装在AggregateException
,这可能会导致一个有趣的一种“瀑布”在您的内部异常可以得到深埋嵌套AggregateException
实例中的例外。因此AggregateException.Flatten
存在理顺后的,其实残局。哦,你应该always pass a TaskScheduler
to ContinueWith
。
这里是第一次尝试,明确指定TaskScheduler
,使用Unwrap
解开嵌套的任务,并避免使用GetAwaiter().GetResult()
的Result
代替嵌套异常:
Task<string> getContent()
{
// I am so sorry, future coder, but I cannot use await here because <insert good reason>
return Task.Run(()=> client.GetAsync(...))
.ContinueWith(t => t.GetAwaiter().GetResult().Content.ReadAsStringAsync(), TaskScheduler.Default).Unwrap()
.ContinueWith(t => t.GetAwaiter().GetResult(), TaskScheduler.Default);
}
如果你在你的代码做了很多,你可能需要使用something like .Then
封装一些这方面的逻辑。哦,一定要记在评论道歉;即使未来的维护者是你自己,这只是做这样的代码礼貌的事情。 ;)
但池的一个线程将被屏蔽
这是不是这样的,因为一旦“先行”的任务已经完成了ContinueWith
委托只调用。 Result
不会阻止,它是免费的,甚至同步。
第二.Result
(ReadAsStringAsync().Result
)挡住,虽然。所以,你必须把它转换成另一种ContinueWith
。
在一般情况下,连续的IO的序列变成ContinueWith
的序列。
在一般情况下,await
最好在这里,但你指出,这不会为你工作。