I'm calling HttpClient
via Async.AwaitTask
, being called from within an agent (MailboxProcessor). I was wanting to catch errors during the HTTP call so used a try...with
in the async workflow, but it completely misses catching client-side timeout exceptions which then cause the agent to crash.
Minimal reproduction:
#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
I can fix it by making it completely syncronous, but would prefer to keep the whole stack async as might be running a lot of these in parallel:
#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!"
Is there an effective way of catching the OperationCanceledException
within an async context?
This happens because the HttpClient.GetStringAsync
task will be cancelled, instead of failing with a TimeoutException
, thus prompting the async mechanism to fire its cancellation continuations, which cannot be handled. A simple way to solve this problem is the following:
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!"
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With