Проблемы с отображением экрана при использовании IJSRuntime Blazor

#blazor #asp.net-core-3.1 #asp.net-core-signalr #blazor-jsinterop

Вопрос:

Прежде всего, я очень сожалею о своем знании английского языка.

В настоящее время я разрабатываю веб-проект с помощью dotnet core 3.1 blazor.

Как и в приведенном ниже источнике, я использую IJSRuntime для вызова функции Javascript, которая занимает много времени.

 [Inject]
IJSRuntime JSRuntime { get; set; }
private async Task BtnDisplay()
    await JSRuntime.InvokeVoidAsync("RefreshWebJS", Data);
}
 

Функция Javascript занимает много времени, поэтому я добавил источник ниже, чтобы добавить счетчик.

 private async Task BtnDisplay()
{
    showLoadingImg = true; // add
    await JSRuntime.InvokeVoidAsync("RefreshWebJS", Data);
    showLoadingImg = false;
}
 

Счетчик определяется на странице razor следующим образом:

 @if (showLoadingImg == true)
{
    <div style="position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; text-align: center;">
    <img src="/images/LoadingImg.gif" style="position: absolute; top: 50%; left: 50%;" />
    </div>
}
 

«StateHasChanged()» или «ожидание вызова синхронизации (() = > StateHasChanged())» также не работает.

Это прекрасно работает, когда я использую Task вместо JSRuntime.

 await Task.Delay(1000); 
 

Почему это не работает, когда я использую JSRuntime.Вызвать voidasync?

Спасибо и извините за то, что читаете трудный английский .

Комментарии:

1. Вы пробовали поместить вызов JSRuntime в его собственную задачу и использовать ContinueWith для отображения loading = false?

2. Да, я пытался «Продолжить», но это не сработало. Метод » Task.delay(1)», предложенный мистером Магу, отлично работает

Ответ №1:

 private async Task BtnDisplay()
{
    showLoadingImg = true; // add
    await Task.Delay(1);
    await JSRuntime.InvokeVoidAsync("RefreshWebJS", Data);
    showLoadingImg = false;
}
 

Blazor автоматически повторно визуализируется при завершении первой Task await асинхронной задачи.

Итак, если это первый Task ваш длительный процесс, он не будет повторно отображаться до тех пор, пока он не завершится.

Добавление await Task.Delay(1) -это простой способ разрешить визуализацию перед вашим длительным процессом.

Дальнейшее Чтение : https://github.com/dotnet/aspnetcore/issues/22159

Это известная особенность работы Blazor, и создатель Blazor также рекомендует этот подход в этой теме (хотя он предпочитает await Task.Yield() — что я всегда забываю использовать!)

Комментарии:

1. Это работает, но звучит как банальное решение. В качестве альтернативы вы можете заставить обработчик событий buttons немедленно вернуться, что приведет к повторному запуску пользовательского интерфейса при запуске давно работающего метода js в новой задаче, которая будет повторно синхронизирована после завершения.

2. Если вы называете понимание того, как работает Blazor, и наилучшее его использование халтурой, тогда конечно. У вас есть пример вашего кода в качестве ответа?

3. без обид, ваш подход совершенно верен, просто я считаю, что это похоже на обходной путь для использования фиктивной задачи таким образом. И да, я приведу пример позже, когда у меня будет больше времени для этого.

4. Теперь с разъяснением в вопросе GitHub и упоминанием задачи. Уступите() Я думаю, что должен извиниться перед вами за то, что поспешил с выводами. Я не знал, что это рекомендуемый способ, спасибо, что поделились им! Что-то в этой «фальшивой» задержке просто как-то подтолкнуло меня, потому что я видел, как такие вещи в разных контекстах использовались странным образом. Задача. Yield() кажется очень разумным и будет более кратким, чем подход к запуску новой задачи для запуска вызова js.

5. Жаль, что Task.Yield() это не всегда помогает.

Ответ №2:

Как упоминалось ранее в комментарии, существует также подход с синхронным обработчиком событий, который запускает взаимодействие JS в новой задаче и возвращается сразу после этого. Вы должны убедиться, что повторно синхронизируете эту новую задачу, когда она будет завершена, с помощью await InvokeAsync(StateHasChanged) :

 private bool ShowLoading = false;

private void HandleButtonClick()
{
    ShowLoading = true;
    Task.Run(async () => await CallLongRunningJs())
        .ContinueWith(t => { ShowLoading = false; InvokeAsync(StateHasChanged); });
}

private async Task CallLongRunningJs()
{
    await jsRuntime.InvokeVoidAsync("longRunningJsFunc", "data...");
}
 

Это более многословный подход, чем подход, Task.Yield() представленный мистером Магу, но я думаю, что для полноты картины стоит упомянуть об этом здесь.

Комментарии:

1. Task.Run() на самом деле ничего не делает на WebAssembly и является стороной сервера для оптимизации. И ContinueWith() устарел. Весь смысл async/await здесь в том, чтобы иметь возможность написать это одним понятным методом, как это сделал @MisterMagoo. В «немедленном возврате»нет никакой выгоды.

2. @HenkHolterman Спасибо вам за эту информацию. Я не рассматривал Blazor WASM, я работаю в основном с сервером Blazor. Я не претендовал на какую-либо дополнительную выгоду от немедленного возврата, кроме того, что это приводит к повторному запуску пользовательского интерфейса до завершения задачи. — По крайней мере, на сервере Blazor. Кстати, откуда вы взяли эту информацию, несмотря на то, что она устарела? Есть ли какое-то официальное заявление или вы имели в виду это как своего рода общий консенсус?

3. Ну, «повторно отправить до завершения задачи» — это своего рода утверждение, но я не вижу в этом смысла. Как вы оцениваете сложность (или читаемость) этого ответа по сравнению с другим ответом? Я поищу ссылку о ContinuWith, но она не официальная. Просто здравый смысл.

4. @HenkHolterman Этот вопрос как раз о том, чтобы попытаться вывести индикатор загрузки до выполнения длительной задачи. В том-то и дело. И да, это решение может не иметь никаких других преимуществ по сравнению с другим. Я бы также оценил его читаемость ниже, конечно! Однако это решение, которое работает для сервера blazor и которое я знаю и использовал раньше в таких случаях, как тот, о котором говорится в вопросе.