2016-05-09 6 views
6

Я звоню HttpClient через Async.AwaitTask, вызывая из агента (MailboxProcessor). Я хотел поймать ошибки во время HTTP-вызова, поэтому в рабочем процессе async использовался try...with, но он полностью пропускает перехватывание исключений тайм-аута на стороне клиента, которые затем приводят к сбою агента.Улавливание тайм-аутов HttpClient в рабочем процессе async

Минимальное воспроизведение:

#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 

я могу это исправить, сделав его полностью Синхронный, но предпочел бы сохранить весь стек асинхр, как может быть запущен много из них параллельно:

#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' только после завершения задачи, так что это по-прежнему истинно async? – danielrbradley

+2

@ danielrbradley Исправить. Это просто заставляет отменять материализацию как исключение, которое может быть обработано асинхронным. – eirik

 Смежные вопросы

  • Нет связанных вопросов^_^