Blazor WebAssembly:如何让 UI 在长 运行、非异步过程中更新
Blazor WebAssembly: How to get UI to update during long running, non-async process
我有一些复杂的计算需要一段时间才能完成。 (是的,我知道,客户端听起来可能不是执行它们的理想场所,但这样做有充分的理由。)我希望页面随着计算的进行而更新结果。随着计算的进行,我可以让 UI 重新渲染,但无法正确更新结果。
请注意,计算 而不是 本质上是异步的,将它们包装在 Task.Run
中似乎没有帮助。这是演示问题的代码的简化版本:
@page "/AsyncTest"
<button type="button" class="btn btn-primary" @onclick="@(e => RunOnClick(e))">Run</button>
<br />
<div>Percent Complete = @PercentComplete %</div>
@code {
private int PercentComplete = 0;
private async Task RunOnClick(MouseEventArgs e) {
for (PercentComplete = 0; PercentComplete < 100; PercentComplete += 20) {
System.Console.WriteLine($"LongCalculation: PercentComplete = {PercentComplete}");
await Task.Run(() => Calculation()); // Does not work.
//await CalculationAsync(); // This works.
StateHasChanged();
}
}
private void Calculation() => System.Threading.Thread.Sleep(500);
private async Task CalculationAsync() => await Task.Delay(500);
protected override void OnAfterRender(bool firstRender) {
System.Console.WriteLine($"OnAfterRender: PercentComplete = {PercentComplete}");
}
}
单击按钮时,UI 上的完成百分比不会更新,直到 所有 处理完成,即从 0% 变为 100%中间没有任何步骤。如果我使用异步版本 CalculationAsync()
,UI 会按预期更新。
控制台输出如下:
blazor.webassembly.js:1 WASM: LongCalculation: PercentComplete = 0
blazor.webassembly.js:1 WASM: OnAfterRender: PercentComplete = 0
blazor.webassembly.js:1 WASM: LongCalculation: PercentComplete = 20
blazor.webassembly.js:1 WASM: OnAfterRender: PercentComplete = 20
blazor.webassembly.js:1 WASM: LongCalculation: PercentComplete = 40
blazor.webassembly.js:1 WASM: OnAfterRender: PercentComplete = 40
blazor.webassembly.js:1 WASM: LongCalculation: PercentComplete = 60
blazor.webassembly.js:1 WASM: OnAfterRender: PercentComplete = 60
blazor.webassembly.js:1 WASM: LongCalculation: PercentComplete = 80
blazor.webassembly.js:1 WASM: OnAfterRender: PercentComplete = 80
blazor.webassembly.js:1 WASM: OnAfterRender: PercentComplete = 100
这表明 UI 正在重新渲染,PercentComplete 属性 正在随着计算的进行而更新。然而,出于某种原因,UI 实际上并没有被改变。我不确定 Blazor 是否以某种方式缓存了 PercentComplete 的值,或者它是否没有意识到它已经更改并跳过了那部分渲染。
知道如何让它工作吗?
注:本题与17069489不同。虽然不是很明显,但该问题涉及 是 异步的任务。
在 Task.Run() 中包装同步版本将在 Blazor/Server 中起作用,但在 Blazor/Wasm 中不起作用。 WebAssembly 在单线程上 运行,这仍然是一个 Browser/Wasm 限制。
将来当 Blazor 在 Wasm 2 上运行并获得真正的线程时,这可能会得到解决。我不知道那个的截止日期。
与此同时,使用额外的 Task.Delay() 使循环至少有点异步:
for (PercentComplete = 0; PercentComplete < 100; PercentComplete += 20)
{
System.Console.WriteLine($"LongCalculation: PercentComplete = {PercentComplete}");
Calculation(); // Make the steps as small as possible, Task.Run is of no use
StateHasChanged();
await Task.Delay(1); // give the UI some time to catch up
}
我有一些复杂的计算需要一段时间才能完成。 (是的,我知道,客户端听起来可能不是执行它们的理想场所,但这样做有充分的理由。)我希望页面随着计算的进行而更新结果。随着计算的进行,我可以让 UI 重新渲染,但无法正确更新结果。
请注意,计算 而不是 本质上是异步的,将它们包装在 Task.Run
中似乎没有帮助。这是演示问题的代码的简化版本:
@page "/AsyncTest"
<button type="button" class="btn btn-primary" @onclick="@(e => RunOnClick(e))">Run</button>
<br />
<div>Percent Complete = @PercentComplete %</div>
@code {
private int PercentComplete = 0;
private async Task RunOnClick(MouseEventArgs e) {
for (PercentComplete = 0; PercentComplete < 100; PercentComplete += 20) {
System.Console.WriteLine($"LongCalculation: PercentComplete = {PercentComplete}");
await Task.Run(() => Calculation()); // Does not work.
//await CalculationAsync(); // This works.
StateHasChanged();
}
}
private void Calculation() => System.Threading.Thread.Sleep(500);
private async Task CalculationAsync() => await Task.Delay(500);
protected override void OnAfterRender(bool firstRender) {
System.Console.WriteLine($"OnAfterRender: PercentComplete = {PercentComplete}");
}
}
单击按钮时,UI 上的完成百分比不会更新,直到 所有 处理完成,即从 0% 变为 100%中间没有任何步骤。如果我使用异步版本 CalculationAsync()
,UI 会按预期更新。
控制台输出如下:
blazor.webassembly.js:1 WASM: LongCalculation: PercentComplete = 0
blazor.webassembly.js:1 WASM: OnAfterRender: PercentComplete = 0
blazor.webassembly.js:1 WASM: LongCalculation: PercentComplete = 20
blazor.webassembly.js:1 WASM: OnAfterRender: PercentComplete = 20
blazor.webassembly.js:1 WASM: LongCalculation: PercentComplete = 40
blazor.webassembly.js:1 WASM: OnAfterRender: PercentComplete = 40
blazor.webassembly.js:1 WASM: LongCalculation: PercentComplete = 60
blazor.webassembly.js:1 WASM: OnAfterRender: PercentComplete = 60
blazor.webassembly.js:1 WASM: LongCalculation: PercentComplete = 80
blazor.webassembly.js:1 WASM: OnAfterRender: PercentComplete = 80
blazor.webassembly.js:1 WASM: OnAfterRender: PercentComplete = 100
这表明 UI 正在重新渲染,PercentComplete 属性 正在随着计算的进行而更新。然而,出于某种原因,UI 实际上并没有被改变。我不确定 Blazor 是否以某种方式缓存了 PercentComplete 的值,或者它是否没有意识到它已经更改并跳过了那部分渲染。
知道如何让它工作吗?
注:本题与17069489不同。虽然不是很明显,但该问题涉及 是 异步的任务。
在 Task.Run() 中包装同步版本将在 Blazor/Server 中起作用,但在 Blazor/Wasm 中不起作用。 WebAssembly 在单线程上 运行,这仍然是一个 Browser/Wasm 限制。
将来当 Blazor 在 Wasm 2 上运行并获得真正的线程时,这可能会得到解决。我不知道那个的截止日期。
与此同时,使用额外的 Task.Delay() 使循环至少有点异步:
for (PercentComplete = 0; PercentComplete < 100; PercentComplete += 20)
{
System.Console.WriteLine($"LongCalculation: PercentComplete = {PercentComplete}");
Calculation(); // Make the steps as small as possible, Task.Run is of no use
StateHasChanged();
await Task.Delay(1); // give the UI some time to catch up
}