Reputation: 21
I am trying to create an async method in Blazor that allows me to run computationally-heavy code asynchronously and then update a component when done, all while keeping the page responsive. The difference between this question and other questions about async in Blazor, is that I am creating a method that takes a long time versus using something like HttpClient's download methods.
The components:
page.blazor
@inject ClassA classA
<button class="btn btn-primary" @onclick="@doWork">Do Work</button>
<Text>@log</Text>
@code {
async Task doWork()
{
log = await classA.doWork();
}
}
C# classes
public class ClassA
{
public async Task<string> doWork()
{
ClassB classB = new ClassB();
var result = await classB.Execute();
return result;
}
}
public class ClassB
{
// This method is meant to take a long time and execute asynchronously
public async Task<string> Execute()
{
string k = "";
for (int i = 0; i < 10000000; i ++)
{
k = i.ToString();
}
return k;
}
}
Note: If I change the code in page.blazor to
@code {
async Task doWork()
{
await Task.Delay(10000);
log = "Done";
}
}
it works properly.
Upvotes: 2
Views: 12342
Reputation: 151
Based on https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.run?view=net-7.0
You can do:
public async Task<string> doWork() { return await Task.Run(()=> yourwork()); }
Upvotes: 1
Reputation: 30330
Updated Answer - see @HenkHolterman's comment.
This code block isn't correct. In Visual Studio you will get the warning shown below.
public async Task<string> Execute()
{
string k = "";
for (int i = 0; i < 10000000; i ++)
{
k = i.ToString();
}
return k;
}
It should look like this:
public Task<string> Execute()
{
string k = "";
for (int i = 0; i < 10000000; i ++)
{
k = i.ToString();
}
return Task.FromResult(k);
}
You don't make something Async by wrapping it in a Task
and telling it to run async
. Execute
is a synchronous block of code wrapped in a Task
. There's no yielding, it just blocks the thread until completion - just like Thread.Sleep(1000).
In Blazor Web Assembly there's ONE thread at present. You can do all the awaits you like on your code. Unless there's a true yield, there's no thread time for the UI and no updates: it all happens at the end!
On the other hand, Task.Delay(xxx)
yields. In simple terms, the Task gets moved down the queue by the thread scheduler letting other code already in the queue execute.
Here's a version of your iterator that will yield.
public async Task<string> Execute()
{
string k = "";
var x = 0;
for (int i = 0; i < 10000000; i++)
{
k = i.ToString();
if (x > 100)
{
await Task.Delay(1);
x = 0;
}
x++;
}
return k;
}
And here's a test page:
@page "/"
<PageTitle>Index</PageTitle>
<h1>Async Test</h1>
<div class="p-2">
Time: @message
</div>
<div class="p-2">
<button class="btn btn-primary" disabled=@go @onclick=ButtonClick>@buttonMessage</button>
</div>
<div class="p-2">
<button class="btn btn-secondary" @onclick=UpdateTime>Get Time</button>
</div>
@code {
private bool go = false;
private string disabled = "";
private string buttonMessage => go ? "Running" : "Go";
private string message = DateTime.Now.ToLongTimeString();
private void UpdateTime()
{
message = DateTime.Now.ToLongTimeString();
}
private async Task ButtonClick()
{
go = true;
await this.Execute();
go = false;
}
public async Task<string> Execute()
{
string k = "";
var x = 0;
for (int i = 0; i < 100000000; i++)
{
k = i.ToString();
if (x > 100)
{
await Task.Delay(1);
x = 0;
}
x++;
}
return k;
}
}
Upvotes: 3