如何捕获使用Async.AwaitTask运行的Task抛出的异常(How to catch exception thrown by Task run with Async.AwaitTask)

编程入门 行业动态 更新时间:2024-10-13 08:23:31
如何捕获使用Async.AwaitTask运行的Task抛出的异常(How to catch exception thrown by Task run with Async.AwaitTask)

编辑 :这结果是一个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”异步函数中的异常,防止它“冒泡”。

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

编辑 :以下是显示问题的屏幕截图: 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设置阻止了异常被捕获?

Edit: This turned out to be an F# bug which can be worked-around by using a custom option type instead of Fsharp's "Option".

In F#, I am trying to call a .net Task with Async.AwaitTask. The task is throwing an exception, and I can't seem to catch it with either try-catch or Async.Catch. And I know of no other way to catch exceptions. What is the solution? And what is the cause of the problem? Thanks for any explanation.

Here is some test code that shows my failure to catch the exception thrown by 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

I need some way to catch the exception inside the "test" async function, preventing it to "bubble up".

I found this related question, but I can't seem to adapt the answer (which deals with Task, not with Task<'a>) to my needs.

Edit: Here is a screenshot showing the problem: https://i.gyazo.com/883f546c00255b210e53cd095b876cb0.png

"ArgumentException was unhandled by user code."

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

Could it be that the code is correct but some Fsharp or Visual Studio setting is preventing the exception from being caught?

最满意答案

TL; DR:异常确实被抓住了,它似乎是在异步中返回None而在这里令人困惑 - 结果是null,而不是None,这搞砸了模式匹配并且通常是一个麻烦。 使用您自己的联合类型而不是Option来处理它。

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

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

实验:

证明我们正在捕捉:将调试输出添加到“with”。 代码到达那里(添加断点或查看调试输出以进行证明)。 我们在Choice1Of2(null)得到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。 仍然是WEIRD。

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

不要使用Option:有趣的是,如果你使用自定义联合类型,它可以完美地运行:

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中),但是异常肯定被捕获,但是None-> null正在搞砸它。

TL;DR: The exception does get caught, it appears to be returning None out of async that's confusing here - the result is null, not None, which is screwing up pattern matching and generally being a bother. Use your own union type instead of Option to handle it.

Edit: Oh, and @takemyoxygen's comments about why you're seeing it straight away is correct. Debug->Exceptions, or just untick "Break when this exception type is user-unhandled" in the pop-up visible in your screenshot. It looks like VS is breaking on THROW, rather than actually on unhandled.

First, returning Some/None inside an Async.Catch makes it useless. Async.Catch returns Choice1Of2(val) unless there's an uncaught exception, so the code as is (if it worked) would return Choice1Of2(Some(string)) with no exception, or Choice1Of2(None) with exception. Either use try/with and handle the exception yourself, or only use Async.Catch.

Experiments:

Proof that we are catching: Add a debug output to the "with". The code DOES get there (add a breakpoint or look at debug output for proof). We get Choice1Of2(null), rather than Choice1Of2(None) in exc though. WEIRD. See image: 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

Remove Async.Catch: Don't use Async.Catch (keep the debug output though). Again, we get to the debug output, so we're catching the exception, and as we're not wrapping in a Choice from Async.Catch, we get null instead of None. STILL WEIRD.

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

Only use Async.Catch: Don't use a try/with, just Async.Catch. This works a bit better. At the pattern match on exc, I have a Choice2Of2 containing the exception. However, when the None is returned out of the outermost async, it becomes 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

Don't use Option: Interestingly, if you use a custom union type, it works perfectly:

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

Using a try/with and no Async.Catch also works with a new 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

It works even if the union is defined as the following:

type UnionDemo = | StringValue of string | ExceptionValue

The following gets null in moreTestRes.

[<EntryPoint>] let main argv = let moreTest = async { return None } let moreTestRes = Async.RunSynchronously moreTest 0

Why this is the case I don't know (still happens in F# 4.0), but the exception is definitely being caught, but None->null is screwing it up.

更多推荐

本文发布于:2023-08-07 17:31:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1465359.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:抛出   异常   AwaitTask   Task   Async

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!