Reputation: 10306
Not sure if I am messed up with my understanding of how async await works, but here is the problem I am stucked at. Consider a contrived example
This code blocks UI
public async void LoginButtonClicked()
{
//create a continuation point so every following statement will get executed as ContinueWith
await Task.FromResult(0);
//this call takes time to execute
Remote.Login("user","password");
}
But this does not (obviously)
public void LoginButtonClicked()
{
Task.Run(()=>{ Remote.Login("user","password");});
}
I like to use method 1 because I don't want to spin long work using a Task.Run rather I prefer framework handle this form me. But the problem is The call to Method 1 seems blocking.
Upvotes: 4
Views: 11237
Reputation: 3503
Every async method has its context.
When the Task starts it might run in a new SynchronizationContext
. "Might" because if the task is already completed, like Task.FromResult(0)
, then no other SynchronizationContext
is created and the original one is used.
Awaiting for a task means that when the task is finished, the next statement will run in the original SynchronizationContext.
This behavior can be changed using the Task.ConfigureAwait(continueOnCapturedContext: false)
. This means that the next statement will continue on the same context. But this will change nothing by doing Task.FromResult(0).ConfigureAwait(false)
because the task is already completed and the original context will be used.
Therefore your Remote.Login("user","password");
will be run in the original context, thus blocking the UI thread which runs on the same context.
If you would have something like:
public async void LoginButtonClicked()
{
await Task.Delay(5000).ConfigureAwait(false);
Remote.Login("user","password");
}
Then Remote.Login("user","password");
would execute in the thread pool context thus being on a different context than the original UI context.
So the best way to fix your code is to create a Remote.LoginAsync()
as stated in @Nicholas W answer.
NOTE on performance: if you have an async method with multiple await statements, and you don't need some of those awaits to do work on UI or web app thread, then you can use Task.ConfigureAwait(false)
in order to prevent multiple switches to the UI/web-app context which slices its execution time.
Upvotes: 2
Reputation: 67
You have to create async version of Remote.Login
async Task LoginAsync(string user, string password)
{
Remote.Login(user, password);
await Task.FromResult(0);
}
and call it
public async void LoginButtonClicked()
{
await LoginAsync("user", "password");
}
Upvotes: -2
Reputation: 2241
Using await/async only stops you from blocking the UI if all the long-running operations you call are async. In your example your Remote.Login
is a synchronous call, so regardless of what the prior await
line does, this will block your UI.
You need to either get an async version of your actual long-running operation (eg something returning a Task
) or if that is not possible, then you can resort to Task.Run
in order to move this work to the ThreadPool
.
What you want if possible:
public async void LoginButtonClicked()
{
await Remote.LoginAsync("user","password");
// do anything else required
}
Upvotes: 5