8

Próbuję wykonać asynchroniczne żądanie WWW na adres URL, który zostanie zwrócony, jeśli żądanie będzie trwało zbyt długo. Używam asynchronicznego workflow F # i biblioteki System.Net.Http, aby to zrobić.Obliczenia asynchroniczne nie przechwytują OperationCancelledException

Nie mogę jednak uchwycić wyjątku Task/OperationCancelledExceptions, które zostały podniesione przez bibliotekę System.Net.Http w przepływie pracy async. Zamiast tego, jest wyjątek w metodzie Async.RunSynchronously, jak widać w tym ślad stosu:

> System.OperationCanceledException: The operation was canceled. at 
> Microsoft.FSharp.Control.AsyncBuilderImpl.commit[a](Result`1 res) 
> at 
> Microsoft.FSharp.Control.CancellationTokenOps.RunSynchronously[a](CancellationToken 
> token, FSharpAsync`1 computation, FSharpOption`1 timeout) at 
> Microsoft.FSharp.Control.FSharpAsync.RunSynchronously[T](FSharpAsync`1 
> computation, FSharpOption`1 timeout, FSharpOption`1 cancellationToken) 
> at <StartupCode$FSI_0004>[email protected]() 

kodu:

#r "System.Net.Http" 

open System.Net.Http 
open System 

let readGoogle() = async { 
    try 
     let request = new HttpRequestMessage(HttpMethod.Get, "https://google.co.uk") 
     let client = new HttpClient() 
     client.Timeout <- TimeSpan.FromSeconds(0.01) //intentionally low to always fail in this example 
     let! response = client.SendAsync(request, HttpCompletionOption.ResponseContentRead) |> Async.AwaitTask 
     return Some response 
    with 
     | ex -> 
      //is never called 
      printfn "TIMED OUT" 
      return None 
} 

//exception is raised here 
readGoogle() 
    |> Async.RunSynchronously 
    |> ignore 
+0

W ogóle, wyjątki są śmieszne z asynchronicznym autonomicznym. Chcesz użyć 'Async.Catch'. Dziwne zachowanie jest spowodowane, gdy wyjątek zostanie ponownie wyświetlony w głównym wątku, powodując nieparzysty ślad stosu. –

+0

Próbowałem pisać to w stylu Async.Catch i problem nadal występuje. Inną rzeczą jest to, że jeśli zostanie zgłoszony inny typ wyjątku, zostanie złapany poprawnie. Zastanawiam się, czy moduł Async obsługuje tokeny anulowania może być wadliwy - być może źle interpretuje wyjątek jako żądanie anulowania do modułu. – WiseGuyEh

Odpowiedz

6

wykreślenie zawsze różni się od błędów. W twoim przypadku można przesłonić domyślne zachowanie AwaitTask że wywołuje „anuluj kontynuację” Jeśli zadanie zostanie anulowane i obsługiwać go różnie:

let readGoogle() = async { 
    try 
     let request = new HttpRequestMessage(HttpMethod.Get, "https://google.co.uk") 
     let client = new HttpClient() 
     client.Timeout <- TimeSpan.FromSeconds(0.01) //intentionally low to always fail in this example 
     return! ( 
      let t = client.SendAsync(request, HttpCompletionOption.ResponseContentRead) 
      Async.FromContinuations(fun (s, e, _) -> 
       t.ContinueWith(fun (t: Task<_>) -> 
        // if task is cancelled treat it as timeout and process on success path 
        if t.IsCanceled then s(None) 
        elif t.IsFaulted then e(t.Exception) 
        else s(Some t.Result) 
       ) 
       |> ignore 
      ) 
     ) 
    with 
     | ex -> 
      //is never called 
      printfn "TIMED OUT" 
      return None 
}