8

Sto provando a fare una richiesta web asincrona a un URL che restituirà se la richiesta impiega troppo tempo. Sto usando il flusso di lavoro asincrono F # e la libreria System.Net.Http per farlo.async computation does not catch OperationCancelledException

Tuttavia, non riesco a rilevare le Task/OperationCancelledException generate dalla libreria System.Net.Http nel flusso di lavoro async. Al contrario, l'eccezione viene sollevata al metodo Async.RunSynchronously, come si può vedere in questa analisi dello stack:

> 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]() 

Il codice:

#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

In generale, le eccezioni sono divertente con asincrono. Vuoi usare 'Async.Catch'. Il comportamento strano è causato quando l'eccezione viene ricostituita sul thread principale causando la traccia dello stack dispari. –

+0

Ho provato a scrivere questo nello stile Async.Catch e il problema si verifica ancora. L'altra cosa è che se viene lanciato un altro tipo di eccezione, viene catturato correttamente. Mi chiedo se il modulo Async gestisce i token di cancellazione potrebbe essere un errore, forse sta interpretando erroneamente l'eccezione come una richiesta di cancellazione al modulo. – WiseGuyEh

risposta

6

cancellazione era sempre diverso dall'errore. Nel tuo caso è possibile ignorare il comportamento predefinito di AwaitTask che invoca "annullare la continuazione" se un'attività viene annullata e gestire in modo diverso:

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 
}