C
C#2y ago
qqdev

❔ This is not a .NET bug, right? (async void)

Hi! Is this by design?
using System;
using System.Threading.Tasks;

public class Program
{
public static void Main()
{
DoStuff();
}

private static async void DoStuff()
{
try
{
await DoAsyncStuff();
}
catch
{
Console.WriteLine("catch");
}
finally
{
Console.WriteLine("finally");
}
}

private static async Task DoAsyncStuff()
{
await Task.Delay(1);
throw new Exception();
}
}
using System;
using System.Threading.Tasks;

public class Program
{
public static void Main()
{
DoStuff();
}

private static async void DoStuff()
{
try
{
await DoAsyncStuff();
}
catch
{
Console.WriteLine("catch");
}
finally
{
Console.WriteLine("finally");
}
}

private static async Task DoAsyncStuff()
{
await Task.Delay(1);
throw new Exception();
}
}
Neither catch nor finally is getting hit. I know that exceptions which are being raised in async void methods are being swallowed but didn't think that those in a try block would get swallowed aswell. For reference: https://stackoverflow.com/a/5383408 https://stackoverflow.com/a/16158374 ^ Those answers state that the exceptions are getting catched
50 Replies
ero
ero2y ago
you're not awaiting DoStuff()
qqdev
qqdev2y ago
Yeah, because you can't
FusedQyou
FusedQyou2y ago
Don't use async void That's your fix
qqdev
qqdev2y ago
async void is "fine" for event handlers
FusedQyou
FusedQyou2y ago
No it's not Don't use async void ever
qqdev
qqdev2y ago
ero
ero2y ago
i mean it's "fine" in the sense that there is literally no other option
FusedQyou
FusedQyou2y ago
GitHub
AspNetCoreDiagnosticScenarios/AsyncGuidance.md at master · davidfow...
This repository has examples of broken patterns in ASP.NET Core applications - AspNetCoreDiagnosticScenarios/AsyncGuidance.md at master · davidfowl/AspNetCoreDiagnosticScenarios
ero
ero2y ago
like for winforms for example you have to use async void for those events
FusedQyou
FusedQyou2y ago
Weird how there is an exception for a feature that is so error-prone But this does not look like an event handler
qqdev
qqdev2y ago
What is the alternative here? I can't change the signature btw My question is rather if this is a .NET bug or not
ero
ero2y ago
it's not
qqdev
qqdev2y ago
Cause the SO posts are stating that it works for them
ero
ero2y ago
the flow leaves Main before anything in it is executed so the program terminates
qqdev
qqdev2y ago
So the SO posts are wrong?
FusedQyou
FusedQyou2y ago
I mean, SO posts are wrong all the time Why can't you change the signature into a Task?
qqdev
qqdev2y ago
Makes sense, was just wondering! ty!
MODiX
MODiX2y ago
Ero#1111
REPL Result: Success
DoStuff();

static async void DoStuff()
{
try
{
await DoAsyncStuff();
}
catch
{
Console.WriteLine("catch");
}
finally
{
Console.WriteLine("finally");
}
}

static async Task DoAsyncStuff()
{
await Task.Delay(1);
throw new Exception();
}
DoStuff();

static async void DoStuff()
{
try
{
await DoAsyncStuff();
}
catch
{
Console.WriteLine("catch");
}
finally
{
Console.WriteLine("finally");
}
}

static async Task DoAsyncStuff()
{
await Task.Delay(1);
throw new Exception();
}
Compile: 613.317ms | Execution: 67.114ms | React with ❌ to remove this embed.
MODiX
MODiX2y ago
Ero#1111
REPL Result: Success
DoStuff();
await Task.Delay(1000);

static async void DoStuff()
{
try
{
await DoAsyncStuff();
}
catch
{
Console.WriteLine("catch");
}
finally
{
Console.WriteLine("finally");
}
}

static async Task DoAsyncStuff()
{
await Task.Delay(1);
throw new Exception();
}
DoStuff();
await Task.Delay(1000);

static async void DoStuff()
{
try
{
await DoAsyncStuff();
}
catch
{
Console.WriteLine("catch");
}
finally
{
Console.WriteLine("finally");
}
}

static async Task DoAsyncStuff()
{
await Task.Delay(1);
throw new Exception();
}
Console Output
catch
finally
catch
finally
Compile: 559.535ms | Execution: 1107.793ms | React with ❌ to remove this embed.
qqdev
qqdev2y ago
It is not my signature 3rd party code
FusedQyou
FusedQyou2y ago
Clone the repo and fix their mistake 😄
ero
ero2y ago
that's a breaking change
qqdev
qqdev2y ago
Easier said than done ;D (in a real world)
FusedQyou
FusedQyou2y ago
I didn't feel like typing out "Clone their repo and create a second proper asynchronous method and make the current one obsolete"
qqdev
qqdev2y ago
So tl;dr is: Exceptions in async void methods are getting swallowed no matter what
Anchy
Anchy2y ago
makes sense to why the code works fine for me on my piece of shit laptop, as I'm getting an output
FusedQyou
FusedQyou2y ago
That's why it's bad
qqdev
qqdev2y ago
No doubt
FusedQyou
FusedQyou2y ago
When in doubt about asynchronous practices, consult this: https://github.com/davidfowl/AspNetCoreDiagnosticScenarios/blob/master/AsyncGuidance.md
GitHub
AspNetCoreDiagnosticScenarios/AsyncGuidance.md at master · davidfow...
This repository has examples of broken patterns in ASP.NET Core applications - AspNetCoreDiagnosticScenarios/AsyncGuidance.md at master · davidfowl/AspNetCoreDiagnosticScenarios
FusedQyou
FusedQyou2y ago
Very interesting explanation on many things
qqdev
qqdev2y ago
It does not cover event handlers tho, right?
ero
ero2y ago
don't use events
qqdev
qqdev2y ago
;D Wait a second. The exception is getting catched if you give it enough time (added a delay to the Main()). Which makes sense The question is why it is not catched in our app then. It is long-running and not immediately exiting
FusedQyou
FusedQyou2y ago
I mean, adding a delay to the main is basically the same as awaiting the task if it had one, without actually handling any exception I would really just update the library you're trying to use What is it anyway?
qqdev
qqdev2y ago
Okay, what could be the scenario here then? <:PB_peepo_think:769654147041067028> App is not exiting immediately, but exception is not getting catched That's prob why the SO posts seem to be somewhat correct (because it works under circumstances)
FusedQyou
FusedQyou2y ago
I mean, I don't think you will find a fix for this You can add a boolean that your async void method handles and your main method will block until it is changed? Can I see the library pls
qqdev
qqdev2y ago
An explanation would be more than enough lul Current fix idea is to use ContinueWith(...) But we are caring about the reason because that has an influence on the fix
FusedQyou
FusedQyou2y ago
What ContinueWith() requires a Task Which you don't have
qqdev
qqdev2y ago
I have one in the async void method Which is why the method is marked with async in the first place
FusedQyou
FusedQyou2y ago
I thought you could not change the method Or the signature
qqdev
qqdev2y ago
One can always add async in this case Don't have to touch the 3rd party code
FusedQyou
FusedQyou2y ago
Oh, like that You have an interface to implement So the whole problem is that you have an interface to abide to, but you have async behaviour
qqdev
qqdev2y ago
public async void MyHandler(...)
{
await DoAsyncStuff(...).ContinueWith(task => ...);
}
public async void MyHandler(...)
{
await DoAsyncStuff(...).ContinueWith(task => ...);
}
FusedQyou
FusedQyou2y ago
You should just keep the method void, not async void, and then store the Task in memory Or, you know, block the whole thing But don't fix it with async void lol
qqdev
qqdev2y ago
Wdym by store the task in memory?
FusedQyou
FusedQyou2y ago
You store the Task retrieved from DoAsyncStuff in memory Or discard it and use ContinueWith to get the result of the task and catch errors There is literally no point in using await here if the whole method is async void
qqdev
qqdev2y ago
Oh yeah, for sure Got ya
FusedQyou
FusedQyou2y ago
Still, the fact that the library does not allow an asynchronous alternative is just bad
qqdev
qqdev2y ago
EventHandler's in a nutshell I guess? ;D
Accord
Accord2y ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.