Blazor WebAssembly:如何在长时间运行的非异步过程中更新UI

时间:2020-03-08 03:48:25

标签: async-await blazor blazor-client-side

我有一些复杂的计算需要一段时间。 (是的,我知道,客户端听起来可能不是理想的选择,但是这样做有充分的理由。)我希望页面随着计算的进行而更新结果。我可以使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不同。尽管不是很明显,但该问题处理的是 异步任务。

1 个答案:

答案 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
}