StateHasChanged() против InvokeAsync(StateHasChanged) в Blazor

Я знаю, что вызов метода StateHasChanged() уведомляет компонент об изменении состояния, и поэтому он должен повторно отрисовываться.

Тем не менее, я также иногда вижу такие вещи, как await InvokeAsync(StateHasChanged) или await InvokeAsync(() => StateHasChanged()) в чужом коде, но я не уверен, чем они отличаются от StateHasChanged() и где один из них должен быть предпочтительнее других и почему.

Единственная информация, которую я смог найти, была этой частью документации Blazor, в которой говорилось, что:

Если компонент необходимо обновить на основе внешнего события, такого как таймер или другие уведомления, используйте метод InvokeAsync, который отправляется в контекст синхронизации Blazor.

Я этого не понимаю. Он просто говорит «... который отправляет в контекст синхронизации Blazor», меня это не устраивает, что такое «контекст синхронизации Blazor»?

Я попытался вызвать StateHasChanged() вместо InvokeAsync(StateHasChanged) в событии TimerElapsed, и он работает, как и ожидалось, без каких-либо проблем. Мне вместо этого звонить await InvokeAsync(StateHasChanged)?! И если да, то почему именно? Я чувствую, что, вероятно, здесь есть какой-то важный нюанс, о котором я не знаю.

Я также видел звонки типа InvokeAsync(() => InvokeAsync(Something)), опять же, почему?

Кроме того, я также иногда вижу, что InvokeAsync() вызывается без await, что с этим делать?!

Извините, слишком много вопросов! :D

Для развертывания Сайтов с использованием Blazor, Angular и React с репозиторием на GitHub на Cloudflare
Для развертывания Сайтов с использованием Blazor, Angular и React с репозиторием на GitHub на Cloudflare
Как развернуть сайты с помощью Blazor, Angular и React с репозиторием на GitHub на Cloudflare.
61
0
23 029
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я попытался вызвать StateHasChanged() вместо InvokeAsync(StateHasChanged) в событии Timer Elapsed, и он работает, как и ожидалось.

Должно быть, это было на WebAssembly. Когда вы попробуете это на Blazor Serverside, я ожидаю исключения. StateHasChanged() проверяет, работает ли он в правильном потоке.

Основная проблема заключается в том, что рендеринг и вызов StateHasChanged должны происходить в основном (UI) потоке. Виртуальный DOM не является потокобезопасным.

Все основные события жизненного цикла Blazor (OnInit, AfterRender, ButtonClick) выполняются в этом специальном потоке, поэтому в редких случаях, когда вам требуется StateHasChanged(), его можно вызвать без InvokeAsync().

Таймер отличается, это «внешнее событие», поэтому вы не можете быть уверены, что он будет выполняться в правильном потоке. InvokeAsync() делегирует работу Blazor SynchronizationContext, что гарантирует его выполнение в основном потоке.

Но Blazor WebAssembly имеет только 1 поток, поэтому в настоящее время внешние события также всегда выполняются в основном потоке. Это означает, что если вы неправильно воспользуетесь этим паттерном Invoke, вы ничего не заметите. До тех пор, пока однажды Blazor Wasm, наконец, не получит настоящие потоки, ваш код не будет работать. Как и в случае с вашим экспериментом с таймером.

Что такое «контекст синхронизации Blazor»?

В .net контекст синхронизации определяет, что происходит с ожиданием (после). Разные платформы имеют разные настройки, контекст синхронизации Blazor во многом похож на контекст WinForms и WPF. В основном по умолчанию стоит .ConfigureAwait(true): возобновить в той же теме.

Иногда я вижу .ConfigureAwait(false) в коде верхнего уровня Blazor Wasm. Это тоже взорвется, когда мы получим настоящие темы. Его можно использовать в службах, вызываемых из blazor, но не для методов верхнего уровня.

И, наконец, await InvokeAsync(StateHasChanged) или await InvokeAsync(() => StateHasChanged()) — это просто лямбда-выражения в C#, ничего общего с Blazor. Первая короткая форма немного эффективнее.

Я также иногда вижу, что InvokeAsync() называется без await

Это будет работать. Вероятно, это лучше, чем другой вариант: сделать вызывающий метод (например, OnTick таймера) async void. Так что используйте это из пути синхронного кода.

Спасибо. Кстати, о вашем заявлении And finally, await InvokeAsync(StateHasChanged or await InvokeAsync(() => StateHasChanged() is just about lambda's in C#, nothing to do with Blazor. Но я имел в виду, что на самом деле я вижу вызов InvokeAsync внутри другого, как в здесь (строка 42)

aradalvand 10.12.2020 09:43

Хм, это тест, не знаю, для чего он нужен. Это не «настоящий код».

H H 10.12.2020 09:48

О, хорошо, приятно знать. Спасибо! Еще одна вещь: вы сказали ...Until one day, when Blazor Wasm finally gets real threads, your code will fail, это действительно произойдет? Или ожидается, что Blazor WebAssembly всегда останется однопоточным? Что, в свою очередь, означало бы, что InvokeAsync всегда будет фактически нерелевантным в Blazor WebAssembly и релевантным только на стороне Blazor Sever.

aradalvand 10.12.2020 09:55

Я думаю, это обсуждается: github.com/dotnet/aspnetcore/discussions/22885

H H 10.12.2020 09:57

Хорошо, так что это еще не решено. Спасибо!

aradalvand 10.12.2020 10:03

Тестовый код, кажется, проверяет это, включая обнаружение ошибок на стороне сервера. Он использует различные способы имитации «внешнего» события из внутреннего нажатия кнопки.

H H 10.12.2020 10:16

@HenkHolterman Если иногда StateHasChanged() вызывает ошибку, не должен ли я всегда использовать await InvokeAsync(StateHasChanged)? Такой как безмозглый вариант. Если нет, то почему?

UserLuke 27.08.2021 15:34

(Поздний ответ:) Нет, я бы не использовал его всегда. Это добавляет немного накладных расходов, но что более важно: это почти никогда не нужно. Хочу обратить внимание на те частные случаи, где это необходимо. Invoke() — это желтый флаг.

H H 03.02.2023 15:27

Кстати, это также относится к Blazor Hybrid с WPF. WPF требует, чтобы обновление пользовательского интерфейса выполнялось в основном потоке. Если StatusHasChanged() вызывается в другом потоке, вместо этого он должен быть заключен в InvokeAsync(StatusHasChanged).

aDisplayName 09.05.2023 17:47

Другие вопросы по теме