2016-11-19 108 views
3

我想用HttpClient在F#中發送POST請求。不幸的是,我發現F#中的超時無效。我有這樣的代碼:HttpClient的超時在異步塊內不起作用

let asyncTest() = async { 
    let httpClient = new HttpClient() 
    try 
     let! res = httpClient.PostAsync("http://135.128.223.112", new StringContent("test"), (new CancellationTokenSource(2000)).Token) |> Async.AwaitTask 
     Console.WriteLine("Test 1") 
    with 
     | :? Exception as e -> Console.WriteLine("Test 2") 
} 

您可以在代碼中看到的IP不存在。我希望呼叫在2秒後取消(取消令牌),但從不拋出異常(「測試2」從不在屏幕上打印)。 PostAsync方法調用程序控制後,永遠不會返回到異步塊。如果我使用HttpClient.Timeout屬性或Async.Catch而不是try塊,行爲是相同的。

在C#中相同的代碼工作正常,兩秒異常引發之後:

private async Task Test() 
{ 
    var httpClient = new HttpClient(); 
    await 
      httpClient.PostAsync("http://135.128.223.112", new StringContent("test"), 
       new CancellationTokenSource(2000).Token); 
} 

我知道異步在F#和C#是不同的,所以我希望這被一些同步問題,僵局造成的。 ..我希望有更深入瞭解F#異步的人可以解釋這一點並提供解決方法。

更新: 我開始F#異步工作是這樣的:

Async.Start (asyncTest()) 

我運行這個測試控制檯appliation和我有ReadKey()的末尾,以便應用程序不會比異步結束更快。

Fyodor的評論後,我試圖將其更改爲RunSynchronously。現在引發異常,但仍需要使用Async.Start。

+3

創建'async'計算,但從來沒有真正啓動。嘗試使用'Async.Start'或'Async.RunSynchronously'或類似的。 –

+0

@FyodorSoikin謝謝我通過Async.Start運行它。我更新了我的問題。 – Michal

回答

5

你們看到的是兩件事情的結果:使用F#異步一個特殊的路徑通過取消延續

  1. OperationCancelledException。實際上,它意味着在async塊的上下文中,它是一個「不可捕獲」的例外,繞過try-with,這對錯誤的延續起作用。
  2. AwaitTask進行積分,你就等着上是你async塊的一部分任務 - 這其中包括提高任務取消到異步取消(OperationCancelledException,而不是TaskCancelledException),並把你的整個工作流程下來。

這不是一個明顯的行爲,它似乎正在改變爲未來版本的F#。

在此期間,你可以嘗試這樣的事情,而不是Async.AwaitTask

let awaitTaskCancellable<'a> (task: Task<'a>) = 
    Async.FromContinuations(fun (cont, econt, ccont) -> 
     task.ContinueWith(fun (t:Task<'a>) -> 
      match t with 
      | _ when t.IsFaulted -> econt t.Exception 
      | _ when t.IsCanceled -> 
       // note how this uses error continuation 
       // instead of cancellation continuation 
       econt (new TaskCanceledException()) 
      | _ -> cont t.Result) |> ignore) 
+0

謝謝@scrwtp的作品完全像你說的... – Michal