Blazor WebAssembly: How to get UI to update during long running, non-async process

半腔热情 提交于 2020-05-30 11:25:49

问题


I have some complex calculations that take a while to do. (Yes, I know, client-side may not sound like the ideal place to do them, but there are good reasons for it.) I would like the page to update with results as the calculations progress. I can get the UI to re-render as the calculations progress, but not update the results correctly.

Note that the calculations are not inherently async and wrapping them in Task.Run does not seem to help. Here is a simplified version of the code that demonstrates the problem:

@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}");
    }
}

When the button is clicked, the Percent Complete on the UI does not update until all the processing is done, i.e. it goes from 0% to 100% without any of the steps in between. If I use async version CalculationAsync() the UI updates as I expect.

Here is the console output:

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

This shows that the UI is being re-rendered and the PercentComplete property is being updated as the calculations progress. For some reason, however, the UI isn't actually being changed. I'm not sure if Blazor is somehow caching the value of PercentComplete or if it doesn't realise that it has changed and skips that bit of the rendering.

Any idea how I can get this to work?

Note: This question is not the same as 17069489. Although not obvious, that question deals with tasks that are async.


回答1:


Wrapping the sync version in Task.Run() will work in Blazor/Server but not in Blazor/Wasm. WebAssembly is running on a single thread, that still is a Browser/Wasm limitation.

This might be resolved in the future when Blazor runs on Wasm 2 and gets real threading. I don't know of a due date for that.

In the mean time, make the loop at least a little bit async with an extra 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
}


来源:https://stackoverflow.com/questions/60584294/blazor-webassembly-how-to-get-ui-to-update-during-long-running-non-async-proce

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!