|
1338 / 918 / 264
Регистрация: 08.08.2014
Сообщений: 2,759
|
||||||
Blazor: асинхронные обработчики событий26.12.2022, 23:41. Показов 1314. Ответов 8
Метки нет (Все метки)
Пример отчасти синтетический, но на нём проблема повторяется и хочется разобраться как делать правильно.
В компоненте есть коллекция, на основании которой рендерится часть шаблона (список). В шаблоне есть 'div', у которого есть два асинхронных обработчика событий - 'onkeydown' и 'onmouseover'. События спамятся в большом количестве. Оба обработчика имеют схожую структуру:
Проблемный сценарий №1 - обработчики конфликтуют между собой на модификации коллекции. Повторяется стабильно. Обойти можно через явные блокировки или конкурентные коллекции. В т.ч. обработчик может конфликтовать сам с собой, если он ушёл в 'await' и не успел завершиться до того, как пришло следующее аналогичное событие. Проблемный сценарий №2 - предполагаемый конфликт между блазоровским методом рендернига и обработчиками событий на той самой коллекции, т.е.: 1. Обрабочтик удалил элемент из коллекции. 2. Выполнил 'await InvokeAsync(StateHasChanged)' и вернул управление. 3. Блазор-движок запустил перерисовку компонента и начал обход коллекции через 'foreach'. 4. И пока он её обходит, любой из обработчиков параллельно выполнил удаление или добавление элемента коллекции. Повторить почему-то не удаётся, хотя вроде как, должно падать. При этом весь код после 'StateHasChanged' в обоих обработчиках точно выполняется асинхронно, т.е. если там добавить тестовые 'await Task.Delay', то видно, что событие 'OnAfterRenderAsync' приходит независимо от того, на каком этапе выполнения находится обработчик.
0
|
||||||
| 26.12.2022, 23:41 | |
|
Ответы с готовыми решениями:
8
Почему не работают обработчики событий для Blazor? Где живут обработчики событий ASP.NET? обработчики событий |
|
1338 / 918 / 264
Регистрация: 08.08.2014
Сообщений: 2,759
|
|
| 27.12.2022, 07:35 [ТС] | |
|
Более реалистичный пример:
1. Сложный компонент с десятком датагридов и кучей других полей для отображения. 2. У компонента, в силу любых причин, запустился очередной рендеринг (который занимает какое-то время). 3. И до того, как завершится п.2, откуда-то из нижних слоёв приложение в компонент пришло событие на актуализацию отображаемых данных. И обработчик события начал модифицировать состояние компонента. И в конце явным образом через 'StateHasChanged' запросил повторную перерисовку. В итоге, в результате п.2 компонент в некоторых случаях может оказываться в некорректном визуальном состоянии, т.к. в процессе рендеринга состояние данных компонента могло измениться. Но это не так критично, т.к. в конце п.3 принудительный рендеринг обновит состояние на актуальное. Но опять же - очевидно, что возможны конфликты при модификации коллекций. И как это обходить? Полностью брать под контроль процесс перерисовки через 'ShouldRender' и через реализацию какого-то менеджера состояний, который будет гарантировать отсутствие конкурентного доступа к ресурсам? Выглядит как-то ну очень сложно с точки зрения массового применения. Делать состояние компонента полностью иммутабельным и целиком подменять его при любых изменениях данных? Т.е. в начале шаблона сохранять актуальный экземпляр состояния компонента в локальную переменную и далее весь рендеринг выполнять по ней. В любых обработчиках, которые меняют состояние, каждый раз создавать новый экземпляр VM и в конце метода просто подменять ссылку на него. Выглядит куда более просто, однако, может порождать чрезмерное количество созданий/удалений экземпляров VM даже при изменении всего одного свойства.
0
|
|
|
2773 / 2073 / 386
Регистрация: 22.07.2011
Сообщений: 7,820
|
||
| 27.12.2022, 16:10 | ||
|
kotelok, ну а чего Вы ожидали , тут как и везде . если у вас многопоточный доступ на запись к ресурсам , то нужно синхронизировать , вашем случае можно на уровне конкурентной коллекции. Т,е задача то типовая и к блазору особо отношения не имеющая , но даже и в случае и с блазором , если обновление компонента вызывается черт его знает из скольких потоков , притом там и порядок может быть произвольным , то без синхронизации получите что-то неопределенное на выходе.
Добавлено через 3 минуты - я бы эти элементы оформил в виде отдельных компанентиков для композиции основного, тогда они смогут обновляться самостоятельно не затрагивая прочее по предназначенным для них событиям.
1
|
||
|
1338 / 918 / 264
Регистрация: 08.08.2014
Сообщений: 2,759
|
|||
| 27.12.2022, 23:01 [ТС] | |||
? Делать имутабельное состояние? Писать свою надстройку на жизненным циклом компонента, подавлять всё через 'ShouldRender' и контролируемо запускать перерисовку в допустимые моменты? В общем, на словах это сложно, придётся экспериментировать.Ну и как ни крути, но всё равно понадобится какой-то менеджер событий между источниками внешних событий и blazor-компонентом, просто чтобы прилетевшие за 500ms 100 событий модели не провоцировали 100 попыток перерендерить шаблон. Добавлено через 2 минуты Добавлено через 4 часа 59 минут Всё странно. Обработчик события элемента DOM-дерева. Начинает выполнение в потоке N (не суть конкретная цифра). Выполняет какие-то действия. Затем у него случается первый 'await' [1]. Код обработчика почему-то приостанавливается. После чего всё в том же потоке N приходят подряд - ShouldRender, лог из начала шаблона, лог из конца шаблона (согласно документации да, он должен тут выполнить рендеринг). Потом снова в потоке N отрабатывает тот код, что в первом 'await' [1]. После чего начинает выполняться (в том же потоке N) следующий 'await' (там для теста Task.Delay). После этого в другом потоке приходит 'OnAfterRenderAsync'. А все последующие инструкции изначального обработчика, включая await-ы, уже выполняются в каком-то третьем потоке. В т.ч. StateHasChanged и всегда неизменно следующие за ним ShouldRender, лог из начала шаблона и лог из конца шаблона. При этом, если в самом шаблоне добавить 'Thread.Sleep', то первый await [1] не выполняется, пока шаблон не будет полностью отрендерен. Добавлено через 5 минут Т.е. если событие пришло от самого Blazor, то он как-то гарантирует, что пока идёт редеринг (от ShouldRender до конца обработки шаблона): 1. Не будут сгенерированы никакие другие события. 2. Все асинхронные обработчики будут каким-то бразом заморожены, пока рендеринг не завершится. Добавлено через 4 минуты Т.е. при таком сценарии, получается, в принципе не возможен конфлит доступа к ресурсам. А вот если событие прилетает откуда-то извне, то именно в процессе рендеринга шаблона можно легко получить исключение 'Collection was modified'.
0
|
|||
|
1338 / 918 / 264
Регистрация: 08.08.2014
Сообщений: 2,759
|
|
| 29.12.2022, 09:28 [ТС] | |
|
В конечном итоге всё свелось к тому же решению, что я когда-то для WPF делал, т.к. проблема ровно та же, что и там (но дополнительно нужно будет как-то реализовать явное управление циклом рендеринга Blazor).
Общая суть - абсолютно любой метод компонента, который хоть как-то меняет состояние компонента, независимо от источника события, отправляется в самописный диспетчер, который выстраивает все поступающие методы в очередь и выполняет их строго последовательно (без блокировки UI-потока). Потому как как-то иначе обеспечить согласованность состояния, можно разве что расставляя повсеместные lock-и, оборачивающие целиком тела всех методов. Хотя, для простых случаев, где согласованность состояния не критична, подобный подход можно и не использовать.
0
|
|
|
Уважайте чужое время
75 / 23 / 8
Регистрация: 01.02.2013
Сообщений: 191
|
||
| 03.02.2023, 16:05 | ||
|
Если Вы будете вместо модификации коллекции прямо в компоненте обработчиками отправлять callback на контроллер (даже если у Вас серверное приложение), который уже будет возвращать компонентам обновлённые данные и сам будет рулить этими объёмами запросов и т.д., то и проблем на рендеринге было бы поменьше. (например пока очередь запросов не рассосалась, рядом с текущим отрендеренным значением делать пометочку о том, что данные могут быть недостоверными в данный момент из-за производящихся расчётов). Если же коллекция меняется чаще, чем успевает обработать рендеринг, то тут просто ничего не сделать, человеческий глаз воспринимает не так много изменений в секунду, как Вам, возможно, хотелось бы + пинг до сервера + сама обработка (а если бы каждый запрос, которых миллионы, обрабатывался N секунд?)... Либо у Вас проблема в архитектуре (почему эта коллекция так часто меняется? В чём её смысл? Может быть можно сделать несколько коллекций, чтобы уменьшить кол-во запросов), либо придётся экстраполировать и отдавать грязные данные, как выше описал. А вообще нормальная практика, когда отрисовка каких-то данных происходит раз в N секунд, даже если фактически данные меняются ежемиллисекундно. Блокировать поток, наверное, тоже может быть приемлемо в некоторых кейсах — это зависит от смысла коллекции. Всё вышенаписанное — имхо.
1
|
||
|
1338 / 918 / 264
Регистрация: 08.08.2014
Сообщений: 2,759
|
||
| 03.02.2023, 17:54 [ТС] | ||
|
Реальный пример - классическое десктопное меню с навигацией при помощи клавиатуры и мыши, реализованное при помощи html-разметки в blazor. Тестовый сценарий: 1. Откройте обычный виндовый Блокнот (VS не подойдёт, у неё меню модифицированное и ведёт себя немного иначе). 2. Активируйте главное меню (так, чтобы отобразилась выпадающая часть). 3. И далее одновременно: 3.1. Зажмите и удерживайте на клавиатуре кнопку "стрелка влево". 3.2. Мышкой водите по элементам главного меню. Задача - получить такую же отзывчивость и скорость перерисовки. Т.е. не раз в секунду, не с паузой, не с требованием делать отдельный клик/нажатие на каждое действие, а вот прямо как в Блокноте. И вот тут и проявляются все эти проблемы одновременной модификации коллекций, т.к.: 1. Одновременно от разных div-ов прилетает два события - "onkeydown" и "onpointerenter". 2. Одновременно начинают отрабатывать их обработчики (асинхронные, разумеется), которые в итоге начинают конкурировать за модификацию состояния меню (перевода его из одного состояния в другое). 3. При этом так же может прилететь какое-то внешнее событие, т.е. вне лайфцикла компонента блазора, согласно которому надо, например, скрыть выпадающую часть меню и снять фокус с меню. И это событие ещё и с процессом рендеринга может законфликтовать (а не только с двумя обозначенными выше событиями).
0
|
||
|
Уважайте чужое время
75 / 23 / 8
Регистрация: 01.02.2013
Сообщений: 191
|
|||
| 03.02.2023, 18:00 | |||
|
Добавлено через 1 минуту
1
|
|||
|
1338 / 918 / 264
Регистрация: 08.08.2014
Сообщений: 2,759
|
|||
| 03.02.2023, 18:14 [ТС] | |||
|
Добавлено через 11 минут
0
|
|||
| 03.02.2023, 18:14 | |
|
Помогаю со студенческими работами здесь
9
Обработчики событий в JS Обработчики событий
Обработчики событий Искать еще темы с ответами Или воспользуйтесь поиском по форуму: |
|
Новые блоги и статьи
|
||||
|
PhpStorm 2025.3: WSL Terminal всегда стартует в ~
and_y87 14.12.2025
PhpStorm 2025. 3: WSL Terminal всегда стартует в ~ (home), игнорируя директорию проекта
Симптом:
После обновления до PhpStorm 2025. 3 встроенный терминал WSL открывается в домашней директории. . .
|
Access
VikBal 11.12.2025
Помогите пожалуйста !! Как объединить 2 одинаковые БД Access с разными данными.
|
Новый ноутбук
volvo 07.12.2025
Всем привет.
По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне:
Ryzen 5 7533HS
64 Gb DDR5
1Tb NVMe
16" Full HD Display
Win11 Pro
|
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
|
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
|
|
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов
На странице:
https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/
нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
|
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов.
. . .
|
Ломающие изменения в C#.NStar Alpha
Etyuhibosecyu 20.11.2025
Уже можно не только тестировать, но и пользоваться C#. NStar - писать оконные приложения, содержащие надписи, кнопки, текстовые поля и даже изображения, например, моя игра "Три в ряд" написана на этом. . .
|
Мысли в слух
kumehtar 18.11.2025
Кстати, совсем недавно имел разговор на тему медитаций с людьми. И обнаружил, что они вообще не понимают что такое медитация и зачем она нужна. Самые базовые вещи. Для них это - когда просто люди. . .
|
Создание Single Page Application на фреймах
krapotkin 16.11.2025
Статья исключительно для начинающих. Подходы оригинальностью не блещут.
В век Веб все очень привыкли к дизайну Single-Page-Application .
Быстренько разберем подход "на фреймах".
Мы делаем одну. . .
|