Note: Turns out this issue is specific to Unity.
I read that async void was to be avoided. I am trying to do so using Result, but my application keeps locking up. How can I avoid using async void?
public async void PrintNumberWithAwait()
{
int number = await GetNumber();
Debug.Log(number); //Successfully prints "5"
}
public void PrintNumberWithResult()
{
int number = GetNumber().Result;
Debug.Log(number); //Application Freezes
}
private async Task<int> GetNumber()
{
await Task.Delay(1000);
return 5;
}
I thought this was correct, but I must be missing something. How do I use async/await without having async void?
I ran my tests separately with the following code (commented one out at a time):
PrintNumberWithAwait();
PrintNumberWithResult();
You misunderstood what is meant by the async void that is to be avoided.
It doesn't mean you should never use a task with no result attached. It just says the asynchronous methods that invoke them should return a Task, not void.
Simply take the signature of your async method from
public async void PrintNumberWithAwait()
and replace void with Task
public async Task PrintNumberWithAwait()
{
int number = await GetNumber();
Debug.Log(number); //Successfully prints "5"
}
Now calling methods have the option of awaiting the result, when and if they choose to. Either:
await PrintNumberWithAwait();
Or
Task t = PrintNumberWithAwait();
// Do other stuff
// ...
await t;
Short Version
Unity's Synchronization Context is single threaded. So:
Detailed Version
You said you are using Unity. Unity is "99%" a single-threaded framework. So I suppose this code is executed on the Main, UI Thread.
Let's step into what your code do in details, when executing PrintNumberWithResult().
Solution Never call .Result.
If you want to fire and forget a task operation use Task.Run(() => ). But Wait! that's not a good idea in Unity! (Maybe it is in other .NET applications).
If you use Task.Run() in Unity you are forcing the async code to be executed using the default .NET Synchronization context, that uses a thread pool, and that could cause some synchronization issues if you are calling some Unity related API.
What you want to do in that case is to use an async void (not really for reasons related to exception handling), an async Task method that you will never await (better), or maybe use a library as UniTask for async await using Unity (The best in my opinion).