C
C#5mo ago
Jason_Bjorn

✅ Double-Async

Is Method1 double async here?
public static async Task<string> Method1()
{
var client = new HttpClient();
Console.WriteLine("in ");
var content = await client.GetStringAsync("https://microsoft.com");
return content;
}

public static Task<string> Method2()
{
var client = new HttpClient();
return client.GetStringAsync("https://microsoft.com");
}
public static async Task<string> Method1()
{
var client = new HttpClient();
Console.WriteLine("in ");
var content = await client.GetStringAsync("https://microsoft.com");
return content;
}

public static Task<string> Method2()
{
var client = new HttpClient();
return client.GetStringAsync("https://microsoft.com");
}
29 Replies
Jason_Bjorn
Jason_BjornOP5mo ago
double because the method itself is async and then async again because I am using await
333fred
333fred5mo ago
No idea what "double async" means to you The method is async because you declared it so. That's it
Jason_Bjorn
Jason_BjornOP5mo ago
What if a method that is declared async calls another method that is also async? @333fred
333fred
333fred5mo ago
Still not sure what double async means to you
Jason_Bjorn
Jason_BjornOP5mo ago
What I just described would be it
333fred
333fred5mo ago
But that doesn't mean anything to me You declared the method async, therefore it's async That's it, end of story
Jason_Bjorn
Jason_BjornOP5mo ago
ok, thanks
333fred
333fred5mo ago
What impact were you imagining that double async would have?
Jason_Bjorn
Jason_BjornOP5mo ago
The outer one would be basically useless since calling a function does not need to be async-ed
333fred
333fred5mo ago
But what impact do you imagine that would have?
Jason_Bjorn
Jason_BjornOP5mo ago
none, really But I guess 2 state machines would be created
333fred
333fred5mo ago
Every async method gets one, and only one, state machine It may have more states when you await, but it's still only one machine
Jason_Bjorn
Jason_BjornOP5mo ago
method 2 seems better anyway since it avoids it all together
C# 1.0
Program.Method1 ()
<Method1>d__0 stateMachine = new <Method1>d__0 ();
stateMachine.<>t__builder = AsyncTaskMethodBuilder<string>.Create ();
stateMachine.<>1__state = -1;
stateMachine.<>t__builder.Start (ref stateMachine);
return stateMachine.<>t__builder.Task;
Program.Method2 ()
HttpClient client = new HttpClient ();
return client.GetStringAsync ("https://microsoft.com");
<Method1>d__0.MoveNext ()
int num = <>1__state;
string result;
try
{
TaskAwaiter<string> awaiter;
if (num != 0)
{
<client>5__1 = new HttpClient ();
Console.WriteLine ("in ");
awaiter = <client>5__1.GetStringAsync ("https://microsoft.com").GetAwaiter ();
if (!awaiter.IsCompleted)
{
num = (<>1__state = 0);
<>u__1 = awaiter;
<Method1>d__0 stateMachine = this;
<>t__builder.AwaitUnsafeOnCompleted (ref awaiter, ref stateMachine);
return;
}
}
else
{
awaiter = <>u__1;
<>u__1 = default(TaskAwaiter<string>);
num = (<>1__state = -1);
}
<>s__3 = awaiter.GetResult ();
<content>5__2 = <>s__3;
<>s__3 = null;
result = <content>5__2;
}
catch (Exception exception)
{
<>1__state = -2;
<client>5__1 = null;
<content>5__2 = null;
<>t__builder.SetException (exception);
return;
}
<>1__state = -2;
<client>5__1 = null;
<content>5__2 = null;
<>t__builder.SetResult (result);
Press Shift+Alt+R to open in ILSpy
C# 1.0
Program.Method1 ()
<Method1>d__0 stateMachine = new <Method1>d__0 ();
stateMachine.<>t__builder = AsyncTaskMethodBuilder<string>.Create ();
stateMachine.<>1__state = -1;
stateMachine.<>t__builder.Start (ref stateMachine);
return stateMachine.<>t__builder.Task;
Program.Method2 ()
HttpClient client = new HttpClient ();
return client.GetStringAsync ("https://microsoft.com");
<Method1>d__0.MoveNext ()
int num = <>1__state;
string result;
try
{
TaskAwaiter<string> awaiter;
if (num != 0)
{
<client>5__1 = new HttpClient ();
Console.WriteLine ("in ");
awaiter = <client>5__1.GetStringAsync ("https://microsoft.com").GetAwaiter ();
if (!awaiter.IsCompleted)
{
num = (<>1__state = 0);
<>u__1 = awaiter;
<Method1>d__0 stateMachine = this;
<>t__builder.AwaitUnsafeOnCompleted (ref awaiter, ref stateMachine);
return;
}
}
else
{
awaiter = <>u__1;
<>u__1 = default(TaskAwaiter<string>);
num = (<>1__state = -1);
}
<>s__3 = awaiter.GetResult ();
<content>5__2 = <>s__3;
<>s__3 = null;
result = <content>5__2;
}
catch (Exception exception)
{
<>1__state = -2;
<client>5__1 = null;
<content>5__2 = null;
<>t__builder.SetException (exception);
return;
}
<>1__state = -2;
<client>5__1 = null;
<content>5__2 = null;
<>t__builder.SetResult (result);
Press Shift+Alt+R to open in ILSpy
333fred
333fred5mo ago
Well, Method2 isn't marked async So it's just a normal method
Jason_Bjorn
Jason_BjornOP5mo ago
yeah, but it seems to behave the same while needing less
333fred
333fred5mo ago
Exception behavior is different
Jason_Bjorn
Jason_BjornOP5mo ago
Which do you favor?
333fred
333fred5mo ago
Method2 will not appear in a stack trace $itdepends
Jason_Bjorn
Jason_BjornOP5mo ago
-_-
333fred
333fred5mo ago
The main question is: what do you want the exception behavior to be? Do you want it to appear in a stack trace? If so, prefer async If you don't care, then prefer non-async
Jason_Bjorn
Jason_BjornOP5mo ago
If I don't use the async method will the exception be lost or what? Will I not be able to find the source of it?
333fred
333fred5mo ago
Depends on the application type and the global sync context, but an unawaited Task could either do nothing much or crash your app An exception from a task-returning method is (usually) observed at the await point My plane is about to take off, so that's probably it for me responding for a while
Angius
Angius5mo ago
The general rule of thumb is $rulesofasync
MODiX
MODiX5mo ago
TLDR on async/await: * every .net API that is suffixed with Async (eg: .Read() and .ReadAsync()) => use the Async version * if the API name ends with Async => await it * if the API returns a Task => await it * if you have to await in a method: * that method must have the async keyword (you could even suffix your function name with Async, up to you) * if it was returning T (eg:string) => it should now return Task<T> (eg: Task<string>) * APIs to ban and associated fix:
var r = t.Result; /* ===> */ var r = await t;
t.Wait(); /* ===> */ await t;
var r = t.GetAwaiter().GetResult(); /* ===> */ var r = await t;
Task.WaitAll(t1, t2); /* ===> */ await Task.WhenAll(t1, t2);
var r = t.Result; /* ===> */ var r = await t;
t.Wait(); /* ===> */ await t;
var r = t.GetAwaiter().GetResult(); /* ===> */ var r = await t;
Task.WaitAll(t1, t2); /* ===> */ await Task.WhenAll(t1, t2);
Angius
Angius5mo ago
While you can return a Task from a method without it being async and without it awaiting anything, in most cases it's not a good idea if only because it disappears from the stack trace
Jason_Bjorn
Jason_BjornOP5mo ago
thank you, have a good flight why does it dissapear from the stacktrace, I thought Tasks are supposed to be bascially like writing blocking code
Angius
Angius5mo ago
The error is raised where the task is awaited, should that task cause an error
Task<int> Foo()
{
return GetNumberAsync();
}

async Task<int> Bar()
{
return await GetNumberAsync();
}

async Task CallFoo()
{
var i = await Foo(); // error happens in `CallFoo`
}

async Task CallBar()
{
var i = await Bar(); // error happens in `Bar`
}
Task<int> Foo()
{
return GetNumberAsync();
}

async Task<int> Bar()
{
return await GetNumberAsync();
}

async Task CallFoo()
{
var i = await Foo(); // error happens in `CallFoo`
}

async Task CallBar()
{
var i = await Bar(); // error happens in `Bar`
}
Jason_Bjorn
Jason_BjornOP5mo ago
good little demo, helps a lot. Thank you
Want results from more Discord servers?
Add your server