簡體   English   中英

F# 異步嘗試...終於做到了! 等待任務

[英]F# async try ... finally do! AwaitTask

問題,簡短:

async{...}表達式中,在try-finally表達式中,我該如何使用do! ... |> Async.AwaitTask do! ... |> Async.AwaitTaskfinally塊中?

問題,長; 失敗的片段:

在等待一系列嵌套的異步任務時,我無法匯總所有異常。

我的設計意圖是每個任務最終都會等待其前一個任務,我希望 Task-Parallel-Library 使用 AggregateException 來組合所有觀察到的異常。

每個異步/任務都可能拋出異常; 以下最小示例鏈接/嵌套兩個異步任務,每個任務都拋出異常:

async {
    let actionBefore =
        async {
            printfn "doing sth before..."
            failwith "! fail during sth before"
        }
        |> Async.StartAsTask

    printfn "doing sth now..."
    failwith "! fail during sth now"

    do! actionBefore |> Async.AwaitTask
}
|> Async.StartAsTask
|> Async.AwaitTask
|> Async.RunSynchronously

我的問題是,在不更改開始和等待順序的情況下,如何添加 try-catch- 或 try-finally- 塊,以便最終的 Async.AwaitTask 報告這兩個異常?

我正在使用 F# 版本 5。

現有答案顯示了關鍵技巧,即使用Task.WhenAll - 與其他選項不同,它等待所有任務並聚合所有異常。 補充一點,我認為你必須在這里混合任務和異步是令人困惑的。 執行此操作的普通 F# 方法是使用Async.StartChild ,但只要第一次計算失敗,就會失敗:

async {
  let! t1 = Async.StartChild <| async {
    printfn "doing sth before..."
    failwith "! fail during sth before" }
  let! t2 = Async.StartChild <| async {
    printfn "doing sth now..."
    failwith "! fail during sth now" }
  let! r1 = t1
  let! r2 = t2
  return r1, r2 }
|> Async.RunSynchronously

沒有Async.WhenAll ,但您可以使用Task.WhenAll定義它。 這是一個更簡單的版本,只有兩個參數:

module Async = 
  let WhenBoth(a1, a2) = async {
    let r1 = a1 |> Async.StartAsTask
    let r2 = a2 |> Async.StartAsTask
    let! _ = System.Threading.Tasks.Task.WhenAll(r1, r2) |> Async.AwaitTask
    return r1.Result, r2.Result }

這樣,您可以以相當干凈的方式編寫您最初想要的內容,只需使用async

async {
  let! t1 = Async.StartChild <| async {
    printfn "doing sth before..."
    failwith "! fail during sth before" }
  let! t2 = Async.StartChild <| async {
    printfn "doing sth now..."
    failwith "! fail during sth now" }
  return! Async.WhenBoth(t1, t2) }
|> Async.RunSynchronously

我不是異步/任務方面的專家,但我認為這里的主要問題是do! actionBefore |> Async.AwaitTask do! actionBefore |> Async.AwaitTask永遠無法執行,因此無法捕獲其異常。

相反,我建議您將每個操作作為單獨的任務開始,然后使用Task.WhenAll來等待它們:

async {
    let actionBefore =
        async {
            printfn "doing sth before..."
            failwith "! fail during sth before"
        }
        |> Async.StartAsTask

    let actionNow =
        async {
            printfn "doing sth now..."
            failwith "! fail during sth now"
        }
        |> Async.StartAsTask

    do! Task.WhenAll(actionBefore, actionNow)
        |> Async.AwaitTask
        |> Async.Ignore
}
|> Async.StartAsTask
|> Async.AwaitTask
|> Async.RunSynchronously

這導致AggregateException包含兩個內部異常,這就是我認為您想要的。

請注意,在這種情況下您不需要外部async ,因此我認為以下版本更簡單:

let actionBefore =
    async {
        printfn "doing sth before..."
        failwith "! fail during sth before"
    }
    |> Async.StartAsTask

let actionNow =
    async {
        printfn "doing sth now..."
        failwith "! fail during sth now"
    }
    |> Async.StartAsTask

Task.WhenAll(actionBefore, actionNow)
    |> Async.AwaitTask
    |> Async.RunSynchronously
    |> ignore

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM