Blazor WebAssembly: как обновить пользовательский интерфейс во время длительного, не асинхронного c процесса - PullRequest
1 голос
/ 08 марта 2020

У меня есть несколько сложных вычислений, которые требуют времени. (Да, я знаю, что на стороне клиента может показаться, что это не идеальное место для их выполнения, но для этого есть веские причины.) Я бы хотел, чтобы страница обновлялась с результатами по мере выполнения вычислений. Я могу заставить пользовательский интерфейс перерисовывать по мере выполнения вычислений, но не корректно обновлять результаты.

Обратите внимание, что вычисления не по своей сути асинхронны c и заключают их в 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}");
    }
}

При нажатии кнопки процент завершения в пользовательском интерфейсе не обновляется до тех пор, пока все обработка не будет выполнена, то есть он идет от 0% до 100% без каких-либо промежуточных шагов. Если я использую asyn c версию CalculationAsync(), пользовательский интерфейс обновляется, как я ожидал.

Вот вывод консоли:

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

Это показывает, что пользовательский интерфейс перерисовывается и свойство PercentComplete обновляется по мере выполнения вычислений. Однако по какой-то причине пользовательский интерфейс фактически не изменяется. Я не уверен, что Blazor каким-то образом кеширует значение PercentComplete или он не осознает, что он изменился, и пропускает этот бит рендеринга.

Есть идеи, как заставить это работать?

Примечание. Этот вопрос отличается от 17069489 . Хотя это и не очевидно, этот вопрос касается задач, которые являются asyn c.

1 Ответ

3 голосов
/ 08 марта 2020

Упаковка версии syn c в Task.Run () будет работать в Blazor / Server, но не в Blazor / Wasm. WebAssembly работает в одном потоке, который по-прежнему является ограничением для браузера / Wasm.

Это может быть решено в будущем, когда Blazor будет работать в Wasm 2 и получит реальную многопоточность. Я не знаю даты для этого.

Тем временем сделайте l oop хотя бы немного асин c с дополнительным 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
}
...