8
nicht

Ich versuche, eine asynchrone Webanforderung an eine URL zu stellen, die zurückgegeben wird, wenn die Anforderung zu lange dauert. Ich verwende den asynchronen F # -Arbeitsablauf und die System.Net.Http-Bibliothek, um dies zu tun.Async-Berechnung fängt OperationCancelledException

Ich bin jedoch nicht in der Lage, die Task/OperationCancelledExceptions abzufangen, die von der System.Net.Http-Bibliothek im async-Workflow ausgelöst werden. Stattdessen wird die Ausnahme bei der Async.RunSynchronously Methode erhoben, wie Sie in diesem Stack-Trace sehen:

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

Den Code:

#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

Im Allgemeinen sind Ausnahmen lustig mit async. Sie möchten 'Async.Catch' verwenden. Das seltsame Verhalten wird verursacht, wenn die Ausnahme auf dem Hauptthread neu ausgelöst wird, was die ungerade Stapelverfolgung verursacht. –

+0

Ich habe versucht, dies im Async.Catch Stil zu schreiben und das Problem tritt immer noch auf. Die andere Sache ist, dass, wenn eine andere Art von Ausnahme ausgelöst wird, wird es korrekt gefangen. Ich frage mich, ob das Async-Modul, das Stornierungs-Token behandelt, möglicherweise fehlerhaft ist - vielleicht interpretiert es die Ausnahme als eine Ablehnungsanforderung für das Modul falsch. – WiseGuyEh

Antwort

6

Stornierung war immer anders als der Fehler. In Ihrem Fall können Sie das Standardverhalten von AwaitTask außer Kraft setzen, die „Abbrechen Fortsetzung“, wenn es Aufgabe wird abgebrochen ruft und zu handhaben anders:

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 
}