我在调用一个恰好为null的嵌套异步时偶然发现了一个问题。引发了一个异常,但无法使用Async工作流提供的任何常规异常处理方法。意外的异步处理行为,异步,可能的错误?
下面是一个简单的测试,重新产生此问题:
[<Test>]
let ``Nested async is null with try-with``() =
let g(): Async<unit> = Unchecked.defaultof<Async<unit>>
let f = async {
try
do! g()
with e ->
printf "%A" e
}
f |> Async.RunSynchronously |> ignore
这导致follwing例外:
System.NullReferenceException : Object reference not set to an instance of an object.
at [email protected](AsyncParams`1 args)
at <StartupCode$FSharp-Core>[email protected](Trampoline this, FSharpFunc`2 action)
at Microsoft.FSharp.Control.Trampoline.ExecuteAction(FSharpFunc`2 firstAction)
at Microsoft.FSharp.Control.TrampolineHolder.Protect(FSharpFunc`2 firstAction)
at Microsoft.FSharp.Control.AsyncBuilderImpl.startAsync(CancellationToken cancellationToken, FSharpFunc`2 cont, FSharpFunc`2 econt, FSharpFunc`2 ccont, FSharpAsync`1 p)
at [email protected]oke(CancellationToken cancellationToken, FSharpFunc`2 cont, FSharpFunc`2 econt, FSharpFunc`2 ccont, FSharpAsync`1 p)
at Microsoft.FSharp.Control.CancellationTokenOps.RunSynchronously(CancellationToken token, FSharpAsync`1 computation, FSharpOption`1 timeout)
at Microsoft.FSharp.Control.FSharpAsync.RunSynchronously(FSharpAsync`1 computation, FSharpOption`1 timeout, FSharpOption`1 cancellationToken)
at Prioinfo.Urkund.DocCheck3.Core2.Tests.AsyncTests.Nested async is null with try-with() in SystemTests.fs: line 345
我真的觉得异常应在这种情况下被抓,或者是这真的是预期的行为? (我使用Visual Studio 2010 SP1备案)
此外,Async.Catch
和Async.StartWithContinuations
展品同样的问题,这表现在这些测试用例:
[<Test>]
let ``Nested async is null with Async.Catch``() =
let g(): Async<unit> = Unchecked.defaultof<Async<unit>>
let f = async {
do! g()
}
f |> Async.Catch |> Async.RunSynchronously |> ignore
[<Test>]
let ``Nested async is null with StartWithContinuations``() =
let g(): Async<unit> = Unchecked.defaultof<Async<unit>>
let f = async {
do! g()
}
Async.StartWithContinuations(f
, fun _ ->()
, fun e -> printfn "%A" e
, fun _ ->())
看来唯一的例外是bind-内提出方法在工作流生成器中,我的猜测是结果是正常的错误处理代码被绕过。它看起来像是在执行异步工作流程时遇到的一个错误,因为我没有在文档或其他地方发现任何暗示这是预期行为的东西。
在大多数情况下,我很容易解决这个问题,至少对我来说这不是一个大问题,但这有点令人不安,因为这意味着您不能完全信任异步异常处理机制捕捉所有的例外。
编辑:
一番考虑我KVB同意后。空的asyncs不应该真正存在于普通代码中,只有当你做了你可能不应该做的事情时(例如使用Unchecked.defaultOf)或使用反射来产生值(在我的情况下,它是一个模拟的框架) 。因此它不是一个真正的bug,而更像是一个边缘案例。
我可以重现它在FSI和控制台应用程序,无论是在VS调试,并从资源管理器启动时的时候。所以它看起来是一致的 –
Option.None基本上是空的。所以看起来像Async <'a option>可以有这个问题。 – Hans