Domanda

Mi sono imbattuto in un problema quando chiamavo un asincrone nidificato che sembra essere nullo. Viene sollevata un'eccezione ma non può essere catturata con nessuno dei normali metodi di gestione delle eccezioni che i flussi di lavoro asincrimale forniscono.

Quello che segue è un semplice test che riproduce il problema:

[<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

che si traduce nell'eccezione follwing:

System.NullReferenceException : Object reference not set to an instance of an object.
at Microsoft.FSharp.Control.AsyncBuilderImpl.bindA@714.Invoke(AsyncParams`1 args)
at <StartupCode$FSharp-Core>.$Control.loop@413-40(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 Microsoft.FSharp.Control.CancellationTokenOps.starter@1121-1.Invoke(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 

Penso davvero che l'eccezione debba essere catturata in questo caso, o questo è davvero il comportamento atteso? (Sto usando Visual Studio 2010 SP1 per il record)

Anche, Async.Catch e Async.StartWithContinuations mostra lo stesso problema dimostrato da questi casi di test:

[<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 _ -> ())

Sembra che l'eccezione sia aumentata all'interno del metodo BIND nel costruttore del flusso di lavoro e la mia ipotesi è che, di conseguenza, il normale codice di gestione degli errori viene bypassato. Mi sembra un bug nell'implementazione di flussi di lavoro asincrimi da quando non ho trovato nulla nella documentazione o altrove che suggeriscono che questo è il comportamento previsto.

È abbastanza facile aggirare nella maggior parte dei casi, quindi non è un grosso problema per me almeno, ma è un po 'inquietante poiché significa che non puoi fidarti completamente del meccanismo di gestione delle eccezioni asincroni per poter catturare tutte le eccezioni .

Modificare:

Dopo aver pensato di essere d'accordo con KVB. Gli asincronizzi nulli non dovrebbero realmente esistere nel codice normale e potrebbero davvero essere prodotti solo se fai qualcosa che probabilmente non dovresti (come usare non controllati.default) o utilizzare la riflessione per produrre i valori (nel mio caso era un framework beffardo coinvolto) . Quindi non è davvero un bug ma più un caso di bordo.

Nessuna soluzione corretta

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top