如何捕获使用Async.AwaitTask运行的任务抛出的异常

时间:2015-09-27 08:58:11

标签: exception asynchronous exception-handling f# task-parallel-library

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

在F#中,我试图用Async.AwaitTask调用.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

我需要一些方法来捕获“test”异步函数中的异常,防止它“冒泡”。

我发现了this相关问题,但我似乎无法调整答案(处理任务,而不是任务&lt;'a&gt;)来满足我的需求。

修改:以下是显示问题的屏幕截图:https://i.gyazo.com/883f546c00255b210e53cd095b876cb0.png

“ArgumentException未被用户代码处理。”

(Visual Studio 2013 ,. NET 4.5.1,Fsharp 3.1,Fsharp.core.dll 4.3.1.0。)

代码是否正确但是某些Fsharp或Visual Studio设置是否阻止异常被捕获?

1 个答案:

答案 0 :(得分:2)

TL; DR:异常被捕获,它似乎是从异步中返回None,这里令人困惑 - 结果是null,而不是None,这是搞乱模式匹配,一般都很麻烦。使用您自己的联合类型而不是Option来处理它。

编辑哦,@ takemyoxygen关于你为何直接看到它的评论是正确的。 Debug-&gt; Exceptions,或者只是取消勾选&#34;当这个异常类型是用户未处理的时候中断&#34;在屏幕截图中显示的弹出窗口中。看起来VS正在打破THROW,而不是实际上未处理。

首先,在Async.Catch中返回Some / None会使它无用。 Async.Catch返回Choice1Of2(val),除非有未捕获的异常,因此代码原样(如果有效)将返回Choice1Of2(Some(string))没有异常,或Choice1Of2(None)异常。使用try / with并自己处理异常,或者只使用Async.Catch。

实验:

证明我们正在捕捉:使用&#34;将调试输出添加到&#34;。代码到达那里(添加断点或查看调试输出以进行证明)。我们得到的是Choice1Of2(null),而不是Choice1Of2(None)。奇怪的。见图片:http://i.imgur.com/e8Knx5a.png

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 
            | _ -> 
                System.Diagnostics.Debug.WriteLine "in with" // We get here.
                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

删除Async.Catch:不要使用Async.Catch(尽管保留调试输出)。同样,我们得到了调试输出,因此我们正在捕获异常,并且由于我们没有从Async.Catch中包装Choice,因此我们得到null而不是None。仍然没有。

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

仅使用Async.Catch:不要使用try / with,只需使用Async.Catch。这样做效果会好一些。在exc上的模式匹配中,我有一个包含异常的Choice2Of2。但是,当从最外面的异步中返回None时,它将变为null。

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

使用try / with并且没有Async.Catch也适用于新的Union:

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

以下在moreTestRes中为null。

[<EntryPoint>]
let main argv = 

    let moreTest = async {
        return None
    }

    let moreTestRes = Async.RunSynchronously moreTest
    0

为什么会出现这种情况我不知道(仍然发生在F#4.0中),但是例外情况肯定会被捕获,但是没有 - >; null正在搞砸它。