我有一些复杂的计算需要一段时间。 (是的,我知道,客户端听起来可能不是理想的选择,但是这样做有充分的理由。)我希望页面随着计算的进行而更新结果。我可以使UI在计算过程中重新呈现,但不能正确更新结果。
请注意,计算本质上是 not 异步的,将它们包装在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不同。尽管不是很明显,但该问题处理的是 异步任务。
答案 0 :(得分:6)
在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
}