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

Tal*_*Guy 3 async-await blazor blazor-client-side

我有一些复杂的计算需要一段时间才能完成。(是的,我知道,客户端听起来可能不是执行它们的理想场所,但有充分的理由。)我希望页面随着计算的进行而更新结果。随着计算的进行,我可以让 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}");
    }
}
Run Code Online (Sandbox Code Playgroud)

单击按钮时,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
Run Code Online (Sandbox Code Playgroud)

这表明 UI 正在重新呈现,并且 PercentComplete 属性正在随着计算的进行而更新。然而,出于某种原因,用户界面实际上并没有被改变。我不确定 Blazor 是否以某种方式缓存了 PercentComplete 的值,或者它是否没有意识到它已经改变并跳过了渲染的那一点。

知道如何让这个工作吗?

注意:此问题与17069489 不同。虽然不是很明显,与任务这个问题的交易,异步。

Hen*_*man 9

在 Task.Run() 中包装同步版本将在 Blazor/Server 中工作,但在 Blazor/Wasm 中无效。WebAssembly 在单线程上运行,这仍然是浏览器/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
}
Run Code Online (Sandbox Code Playgroud)