F# async 尝试...终于可以了!等待任务
F# async try ... finally do! AwaitTask
问题,简而言之:
在 async{...}
表达式中,在 try-finally
表达式中,如何在 finally
块中使用 do! ... |> Async.AwaitTask
?
问题,长;失败的片段:
我在等待嵌套异步任务链时无法聚合所有异常。
我的设计意图是每个任务最终都会等待它的先行任务,我希望任务并行库使用 AggregateException 来组合所有观察到的异常。
每个async/task都可能抛出异常;以下最小示例 chains/nests 两个异步任务,每个任务抛出一个异常:
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-blocks 以便最终 Async.AwaitTask 报告这两个异常?
我正在使用 F# 版本 5。
我不是 async/task 方面的专家,但我认为这里的主要问题是 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
现有答案显示了关键技巧,即使用 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
问题,简而言之:
在 async{...}
表达式中,在 try-finally
表达式中,如何在 finally
块中使用 do! ... |> Async.AwaitTask
?
问题,长;失败的片段:
我在等待嵌套异步任务链时无法聚合所有异常。
我的设计意图是每个任务最终都会等待它的先行任务,我希望任务并行库使用 AggregateException 来组合所有观察到的异常。
每个async/task都可能抛出异常;以下最小示例 chains/nests 两个异步任务,每个任务抛出一个异常:
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-blocks 以便最终 Async.AwaitTask 报告这两个异常?
我正在使用 F# 版本 5。
我不是 async/task 方面的专家,但我认为这里的主要问题是 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
现有答案显示了关键技巧,即使用 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