在异步工作流中捕获HttpClient超时

Dan*_*ley 7 f# dotnet-httpclient f#-async

我打电话HttpClient通过Async.AwaitTask,从代理(MailboxProcessor)中被调用.我想在HTTP调用期间捕获错误,因此try...with在异步工作流中使用了a ,但它完全错过捕获客户端超时异常,然后导致代理崩溃.

最小复制:

#r "System.Net.Http"
open System
open System.Net.Http

let client = new HttpClient()
client.Timeout <- TimeSpan.FromSeconds(1.)
async {
    try
        let! content = Async.AwaitTask <| client.GetStringAsync("http://fake-response.appspot.com/?sleep=30")
        return content
    with ex ->
        // Does not catch client-side timeout exception
        return "Caught it!"
}
|> Async.RunSynchronously
// Throws System.OperationCanceledException: The operation was canceled
Run Code Online (Sandbox Code Playgroud)

我可以通过使它完全同步来修复它,但更愿意保持整个堆栈异步,因为可能并行运行大量这些:

#r "System.Net.Http"
open System
open System.Net.Http

let client = new HttpClient()
client.Timeout <- TimeSpan.FromSeconds(1.)
try
    Async.AwaitTask <| client.GetStringAsync("http://fake-response.appspot.com/?sleep=30")
    |> Async.RunSynchronously
with ex ->
    "Caught it!"
// Returns "Caught it!"
Run Code Online (Sandbox Code Playgroud)

是否有一种有效的方法来捕获OperationCanceledException异步上下文?

eir*_*rik 7

发生这种情况是因为HttpClient.GetStringAsync任务将被取消,而不是失败TimeoutException,从而提示异步机制触发其取消延续,这是无法处理的.解决此问题的简单方法如下:

async {
    try
        let! content = 
            client.GetStringAsync("http://fake-response.appspot.com/?sleep=30")
                  .ContinueWith(fun (t:Task<string>) -> t.Result)
            |> Async.AwaitTask
        return content
    with ex ->
        // Does not catch client-side timeout exception
        return "Caught it!"
}
Run Code Online (Sandbox Code Playgroud)

  • @danielrbradley正确.这仅仅强制取消实现为可以由异步处理的异常. (2认同)