2016-05-09 45 views
6

我通過Async.AwaitTask調用HttpClient,從代理(MailboxProcessor)中調用。我希望在HTTP調用期間捕獲錯誤,因此在異步工作流中使用了try...with,但它完全沒有捕獲客戶端超時異常,從而導致代理崩潰。在異步工作流中捕獲HttpClient超時

最小再現:

#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 

我可以使它完全syncronous修復它,但希望保持作爲可能會並行運行很多的這些全棧異步:

#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!" 

在異步上下文中捕獲OperationCanceledException是否有效?

+0

這似乎是''Async.Catch''打算做的事情,除了它實際上沒有發現異常 - 行爲是一樣的作爲OP的例子。 –

回答

5

發生這種情況是因爲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!" 
} 
+0

太棒了!所以,爲了確保我正確理解這一點 - ContinueWith處理任務何時取消?並且只有在任務完成後才調用'.Result',所以它仍然是真正的異步? – danielrbradley

+2

@danielrbradley正確。這只是強制取消作爲可以通過異步處理的異常實現。 – eirik