Mic*_*hal 3 f# asynchronous c#-to-f# async-await f#-async
我正在尝试使用HttpClient在F#中发送POST请求.不幸的是,我发现F#中的超时不起作用.我有这样的代码:
let asyncTest() = async {
let httpClient = new HttpClient()
try
let! res = httpClient.PostAsync("http://135.128.223.112", new StringContent("test"), (new CancellationTokenSource(2000)).Token) |> Async.AwaitTask
Console.WriteLine("Test 1")
with
| :? Exception as e -> Console.WriteLine("Test 2")
}
Run Code Online (Sandbox Code Playgroud)
您在代码中可以看到的IP不存在.我希望2秒后取消呼叫(取消令牌),但永远不会抛出异常("测试2"永远不会在屏幕上打印).在PostAsync方法调用之后,程序控制永远不会返回到异步块.如果我使用HttpClient.Timeout属性或Async.Catch而不是try块,行为是相同的.
在C#中,相同的代码按预期工作,引发两秒异常:
private async Task Test()
{
var httpClient = new HttpClient();
await
httpClient.PostAsync("http://135.128.223.112", new StringContent("test"),
new CancellationTokenSource(2000).Token);
}
Run Code Online (Sandbox Code Playgroud)
我知道F#和C#中的异步是不同的所以我希望这是由一些同步问题引起的,死锁......我希望有更深入了解F#async的人可以解释这个并提供解决方法.
更新:我正在开始像这样的F#异步工作:
Async.Start (asyncTest())
Run Code Online (Sandbox Code Playgroud)
我在控制台应用程序中运行此测试,最后我有ReadKey(),因此应用程序不会比异步更快结束.
在评论Fyodor的评论后,我试图将其更改为RunSynchronously.现在引发异常,但我仍然需要它与Async.Start一起工作.
你所看到的是两件事的结果:
OperationCancelledException通过取消延续使用F#异步中的特殊路径.它在实践中意味着在async块的上下文中,它是一个"无法捕获"的异常,绕过try-with,它可以在错误延续中起作用.AwaitTask将您正在等待的任务集成到您的async块中 - 包括将任务取消提升为异步取消(OperationCancelledException而不是TaskCancelledException)并降低整个工作流程.这不是一个明显的行为,它似乎正在为未来版本的F#进行更改.
在此期间,你可以尝试这样的事情,而不是Async.AwaitTask:
let awaitTaskCancellable<'a> (task: Task<'a>) =
Async.FromContinuations(fun (cont, econt, ccont) ->
task.ContinueWith(fun (t:Task<'a>) ->
match t with
| _ when t.IsFaulted -> econt t.Exception
| _ when t.IsCanceled ->
// note how this uses error continuation
// instead of cancellation continuation
econt (new TaskCanceledException())
| _ -> cont t.Result) |> ignore)
Run Code Online (Sandbox Code Playgroud)