Reputation: 1251
The following code works perfectly. It shows the spinner on the UI, starts a task using a thread from the threadpool and runs the heavy operation, once complete, logic to hide the spinner executes on the main thread as intended.
public void LoadCustomers()
{
// Update UI to show spinner
this.LoadingCustomers = true;
Task.Run(async () =>
{
var customers = await this.custService.GetCustomers();
// code truncated for clarity
Device.BeginInvokeOnMainThread(() =>
{
// Update UI to hide spinner
this.LoadingCustomers = false;
});
});
}
My question; Is there a better way to write this logic using ContinueWith/ConfigureAwait options? Using these options seems to block the UI thread. In the example below, shouldn't the UI thread continue running the UI logic (animating the spinner/user input) and then come back to complete the logic inside the ContinueWith?
public void LoadCustomers()
{
// Update UI to show spinner
this.LoadingCustomers = true;
this.custService.GetCustomers().ContinueWith((t) =>
{
var customers = t.Result;
// code truncated for clarity
// Update UI to hide spinner
this.LoadingCustomers = false;
});
}
As requested in the comments, here is the code for GetCustomers. the dbContext is EntityFrameworkCore.
public async Task<List<CustomerModel>> GetCustomers()
{
return await this.dbContext.Customers.ToListAsync();
}
UPDATE
The answer by FCin is correct, however; the cause root of this seems to be with EFCore and ToListAsync, it isn't running asynchronously.
Upvotes: 4
Views: 3988
Reputation: 7122
EDIT: @bradley-uffner suggested to just write the following:
public async Task LoadCustomers()
{
// Update UI to show spinner
this.LoadingCustomers = true;
var customers = await this.custService.GetCustomers();
// code truncated for clarity
// you are still on UI thread here
this.LoadingCustomers = false;
}
How about this:
public async Task LoadCustomers()
{
// Update UI to show spinner
this.LoadingCustomers = true;
await Task.Run(async () =>
{
var customers = await this.custService.GetCustomers();
// code truncated for clarity
});
this.LoadingCustomers = false;
}
The code after await
is executed on the current thread so it should work out of the box.
Upvotes: -1
Reputation: 3915
Proper way of writing such method is to use async/await
from start to finish. Right now you are doing fire and forget meaning if there is exception inside Task.Run
you will never know about it. You should start from an event handler. This can be whatever, mouse click, page loaded, etc.
private async void MouseEvent_Click(object sender, EventArgs args)
{
await LoadCustomers();
}
public async Task LoadCustomers()
{
// Update UI to show spinner
this.LoadingCustomers = true;
// We don't need Device.BeginInvokeOnMainThread, because await automatically
// goes back to calling thread when it is finished
var customers = await this.custService.GetCustomers();
this.LoadingCustomers = false;
}
There is an easy way to remember when to use Task.Run
. Use Task.Run
only when you do something CPU bound, such as calculating digits of PI.
Upvotes: 8