Exception 如何捕获使用Async.AwaitTask运行的任务引发的异常

Exception 如何捕获使用Async.AwaitTask运行的任务引发的异常,exception,asynchronous,exception-handling,f#,task-parallel-library,Exception,Asynchronous,Exception Handling,F#,Task Parallel Library,编辑:这是一个F#bug,可以通过使用自定义选项类型而不是Fsharp的“选项”来解决 在F#中,我尝试使用Async.WaitTask调用.net任务。任务正在抛出异常,我似乎无法用try-catch或Async.catch捕获它。我知道没有其他方法可以捕捉异常。解决办法是什么?问题的原因是什么?谢谢你的解释 以下是一些测试代码,显示我未能捕获DownloadStringTaskAsync引发的异常: open System open System.Net [<EntryPoint&g

编辑:这是一个F#bug,可以通过使用自定义选项类型而不是Fsharp的“选项”来解决

在F#中,我尝试使用Async.WaitTask调用.net任务。任务正在抛出异常,我似乎无法用try-catch或Async.catch捕获它。我知道没有其他方法可以捕捉异常。解决办法是什么?问题的原因是什么?谢谢你的解释

以下是一些测试代码,显示我未能捕获DownloadStringTaskAsync引发的异常:

open System
open System.Net

[<EntryPoint>]
let main argv = 

    let test =
        async{
        let! exc = Async.Catch( async{
            try
                let w = new Net.WebClient();
                let! str = Async.AwaitTask (w.DownloadStringTaskAsync "") // throws ArgumentException
                return Some str
            with 
            | _ -> 
                return None // not caught
            }
            )
        match exc with
        | Choice1Of2 r -> return r
        | Choice2Of2 ext -> return None  // not caught
        }

    let res = Async.RunSynchronously(test)
    let str = Console.ReadLine();
    0 // return an integer exit code
开放系统
开放系统.Net
[]
让主argv=
试一试=
异步的{
让!exc=Async.Catch(异步{
尝试
设w=new Net.WebClient();
let!str=Async.AwaitTask(w.downloadstringtaskancy“”)//引发ArgumentException
返回一些str
具有
| _ -> 
返回无//未捕获
}
)
将exc与
|选择1OF2 r->返回r
|选择2of2 ext->return None//not catch
}
让res=Async.RunSynchronously(测试)
让str=Console.ReadLine();
0//返回整数退出代码
我需要某种方法来捕获“test”异步函数中的异常,防止它“冒泡”


我找到了相关的问题,但我似乎无法调整答案(它处理的是任务,而不是任务TL;DR:异常确实会被捕获,它似乎在异步中返回None,这在这里很混乱-结果是null,而不是None,这会破坏模式匹配,通常是一个麻烦。使用您自己的联合类型而不是选项来处理它

编辑:哦,@takemyoxygen关于为什么你马上看到它的评论是正确的。调试->异常,或者在屏幕截图中可见的弹出窗口中取消勾选“当此异常类型为用户未处理时中断”。看起来VS在抛出时中断,而不是实际上在未处理时中断

首先,在Async.Catch中返回一些/None会使其无效。Async.Catch返回
Choice1Of2(val)
,除非存在未捕获的异常,否则代码原样(如果它工作)将返回
Choice1Of2(一些(字符串))
而没有异常,或
Choice1Of2(None)
带有异常。请使用try/with并自行处理异常,或者仅使用Async.Catch

实验: 我们正在捕获的证明:将调试输出添加到“with”。代码确实到达了那里(添加断点或查看调试输出以进行证明)。我们得到的是
Choice1Of2(null)
,而不是exc中的
Choice1Of2(None)
。奇怪。请参见图片:

以下内容在moreTestRes中为null

[<EntryPoint>]
let main argv = 

    let moreTest = async {
        return None
    }

    let moreTestRes = Async.RunSynchronously moreTest
    0
[]
让主argv=
让moreTest=async{
一无所获
}
让moreTestRes=Async.RunSynchronously moreTest
0

我不知道为什么会出现这种情况(仍然发生在F#4.0中),但异常肯定会被捕获,但None->null会把它搞砸。

很抱歉让您失望,但这段代码对我来说非常好(在试用中会捕获异常)。使用F#3.1。我不需要。请参见截图:Visual Studio 2013、.net framework 4.5.1、Fsharp 3.1、Fsharp.core.dll 4.3。1@seguso,visual studio调试器中断并不意味着异常未处理。请尝试在不使用调试器的情况下运行应用程序,它是否会崩溃?是否也可以转到
Debug->Windows->exception Settings
并查看是否在那里检查了
ArgumentException
?@takemyoxigen:我被Visual Studio说“ArgumentException未经用户代码处理”的事实误导了。请看另一个屏幕截图:但是,如果我按“继续”,难以置信的是,程序会正确终止。因此,您是对的。但是,在Visual Studio 2013中,我没有“调试”>“Windows”>“异常”设置,因此我不明白发生了什么。@seguso抱歉,我的错。在VS 2013中,它是“调试”>“异常”,因此概括起来,1)这是f#中的错误,而不是我的代码中的错误。问题是res=null而不是None。2)如果我使用自定义联合而不是option,那么问题就消失了。Res=MyCustomNone,不为空。3) 但是,即使我使用自定义联合而不是无,调试器中断并错误地说“未处理的异常”的问题仍然存在:正确吗?从技术上讲,您的问题的答案是捕获了异常。您看到异常是因为您总是在该异常上中断。取消选中图像中可见的复选框,单击“确定/继续”,然后再次尝试运行代码–您不应该在vs中引发异常,只是它会被捕获到。我剩下的回答包括为什么exc上的模式匹配不起作用,等等——这似乎是由一个F#bug引起的。你说:“你总是在那个异常上打破”。对不起,我觉得不是这样的。复选框显示“当用户未处理此异常类型时中断”。它并没有说“永远休息”。我正在使用try-catch处理异常,因此在我看来,该对话框不应该出现。如果单击“全部重置”,是否为未处理的用户勾选了参数异常?另外,如果您转到工具->选项,调试->常规并取消选中“仅我的代码”,会发生什么?VS是否在抛出异常后仍会抱怨?DownloadStringTaskAsync是您正在调用的东西,它正在抛出异常,异常将进入Async.AwaitTask(这不是您的代码)。这意味着您的异常被发送到非用户代码-因此您有一个未处理的用户中断,因为您在异常到达非用户代码之前没有处理它。禁用“仅我的代码”意味着它将让异常冒泡通过框架代码,并且只有在真正未处理的情况下才会中断。
open System
open System.Net

[<EntryPoint>]
let main argv = 

    let test = async {
        try
            let w = new Net.WebClient();
            let! str = Async.AwaitTask (w.DownloadStringTaskAsync "") // throws ArgumentException
            return Some str
        with 
        | _ -> 
            System.Diagnostics.Debug.WriteLine "in with"
            return None }

    let res = Async.RunSynchronously(test)
    let str = Console.ReadLine();
    0 // return an integer exit code
open System
open System.Net

[<EntryPoint>]
let main argv = 

    let test = async {
        let! exc = Async.Catch(async {
            let w = new Net.WebClient();
            let! str = Async.AwaitTask (w.DownloadStringTaskAsync "") // throws ArgumentException
            return str })
            
       match exc with      
       | Choice1Of2 v -> return Some v
       | Choice2Of2 ex -> return None  
       }

    let res = Async.RunSynchronously(test)
    let str = Console.ReadLine();
    0 // return an integer exit code
open System
open System.Net

type UnionDemo =
    | StringValue of string
    | ExceptionValue of Exception

[<EntryPoint>]
let main argv = 

    let test = async {
        let! exc = Async.Catch(async {
            let w = new Net.WebClient();
            let! str = Async.AwaitTask (w.DownloadStringTaskAsync "") // throws ArgumentException
            return str })
            
       match exc with      
       | Choice1Of2 v -> return StringValue v
       | Choice2Of2 ex -> return ExceptionValue ex 
       }

    let res = Async.RunSynchronously(test)
    let str = Console.ReadLine();
    0 // return an integer exit code
open System
open System.Net

type UnionDemo =
    | StringValue of string
    | ExceptionValue of Exception

[<EntryPoint>]
let main argv = 

    let test = async {
        try
            let w = new Net.WebClient();
            let! str = Async.AwaitTask (w.DownloadStringTaskAsync "") // throws ArgumentException
            return StringValue str
        with
        | ex -> return ExceptionValue ex }

    let res = Async.RunSynchronously(test)
    let str = Console.ReadLine();
    0 // return an integer exit code
type UnionDemo =
    | StringValue of string
    | ExceptionValue
[<EntryPoint>]
let main argv = 

    let moreTest = async {
        return None
    }

    let moreTestRes = Async.RunSynchronously moreTest
    0