|
1341 / 920 / 265
Регистрация: 08.08.2014
Сообщений: 2,775
|
|
Долгие асинхронные операции и очень быстрый пользователь14.12.2021, 17:43. Показов 4536. Ответов 36
Метки нет (Все метки)
Минимальный пример:
1. В UI есть поле ввода. 2. Пользователь вносит изменения в данные. Срабатывает событие. 3. По событию нужно: 3.1. Отправить асинхронный запрос на сервер. 3.2. По полученным результатам обновить часть свойств VM (забинденных на View). В т.ч. несколько коллекций смерджить. И пункты 3.1-3.2 могут быть относительно долгими. И пока они не завершились, пользователь может ещё что-нибудь ввести и спровоцировать повторный вызов 3.1-3.2. Более того, технически возможно, что обработчик от первого срабатывания завершится позже второго. Т.е. вариант - не трогать общие коллекции, а просто по завершению подменять целевую тоже не подходит как решение (да и View на подобные подмены плохо реагирует). Добавлять задержку и инициировать событие только после окончания ввода смысла не имеет. Это лишь снизит количество срабатываний п.3 при быстром непрерывном вводе. Блокировать ввод до окончания обработки - плохая идея, интерфейс станет неотзывчивым и станет вести себя странно (например, пользователь поставил фокус в combobox и стрелками клавиатуры жмёт вверх/вниз много раз подряд, если при этом combobox будет сериться/блокироваться, это будет неприятно для пользователя). В общем виде решение сейчас выглядит следующим образом - если один обработчик уже запущен, то: 1. Все последующие просто ставятся в очередь. 2. Когда первый обработчик завершается, из очереди берётся последний, запускается на обработку, а все остальные просто выбрасываются (события не содержат полезной нагрузки, обработчик события ориентируется только на актуальное состояние VM, потому их можно отбросить). Проблему это решает. Но в данном случае при помощи велосипеда (самописный менеджер, который эти события собирает, идентифицирует, группирует, корректно отбрасывает лишние и отдаёт в обработку последнее после завершения предыдущего). Вопросы: 1. Можно ли эту проблему решить через дефолтный функционал ReactiveUI? Сразу оговорюсь, что 'Throttle' тут не поможет, он для другого, он именно для реализации задержки после окончания ввода. 2. Может есть уже какие-то готовые/проверенные паттерны/подходы для этого? Глобальные шины/аггрегаторы событий тут не особо подходят, т.к. это всё же внутренняя кухня VM, которой нужно как-то разрулить ввод пользователя и результаты обработки данных в Модели. 3. Может у меня вообще подход неправильный? Добавлено через 19 минут немного подробностей
Пример и варианты того, что не подходит. Панель с параметрами для ввода/выбора, например, панель фильтра - выбирается дата, вводится какой-то текст, выбирается статус из комбобокса и ставится какой-то чекбокс. И по всему этому набору параметров надо с сервера запросить данные и отобразить их в таблице (это простой пример, в реальности там может быть сложный апдейт нескольких связанных коллекций для отображения). Типовые варианты решения: 1. Пользователь ввёл все параметр, нажал кнопку "загрузить", все элементы ввода стали не активными (и кнопка тоже), данные обновились, элементы снова стали доступны. Этот вариант не подходит, нужно именно в режиме живого ввода табличные данные с сервера обновлять. 2. Без кнопки "загрузить" при изменении любого параметра делать то же самое - деактивировать, загружать, активировать. Не подходит по той же причине, UI будет мигать и вести себя некрасиво. 3. При любых изменениях запускать в фоне процесс загрузки и как-то управлять повторными событиями. Этот вариант подходит с точки зрения UI, именно его я пытаюсь сейчас реализовать, именно про него основные вопросы, т.к. пока "костыли и велосипеды".
0
|
|
| 14.12.2021, 17:43 | |
|
Ответы с готовыми решениями:
36
Unattend.xml и очень долгие обновления Асинхронные операции на C#
|
|
1341 / 920 / 265
Регистрация: 08.08.2014
Сообщений: 2,775
|
|
| 15.12.2021, 16:17 [ТС] | |
|
И у вас Andrey-MSK, и у вас limeniye неожиданное для меня использование DI-контейнера.
Получается, что VM имеет к нему доступ через статик-класс? Т.е. VM внутри себя может запросить любой нужный ей сервис. Есть какие-то преимущества у такого подхода? Я просто всегда считал, что VM должна через конструктор определять свой контракт и, соответственно, получать все необходимые ей зависимости исключительно через конструктор.
0
|
|
|
|
|||||||||||
| 15.12.2021, 16:22 | |||||||||||
|
kotelok, вот полностью BaseVM, она имеет ссылку на слой Services, который вращается вообще отдельно от всего
![]()
А вот ServiceLocator, который многие считают антипаттерном ![]()
Хотя постоянно его используют в WPF
2
|
|||||||||||
|
1341 / 920 / 265
Регистрация: 08.08.2014
Сообщений: 2,775
|
||
| 15.12.2021, 16:26 [ТС] | ||
|
0
|
||
|
1182 / 624 / 160
Регистрация: 19.04.2018
Сообщений: 2,923
|
|
| 15.12.2021, 16:36 | |
|
0
|
|
|
|
|
| 15.12.2021, 16:37 | |
|
0
|
|
|
1182 / 624 / 160
Регистрация: 19.04.2018
Сообщений: 2,923
|
||
| 15.12.2021, 16:39 | ||
|
А то, помню, ссылки от App.xaml.cs через 20 классов пробрасывал
1
|
||
|
1341 / 920 / 265
Регистрация: 08.08.2014
Сообщений: 2,775
|
|||
| 15.12.2021, 16:55 [ТС] | |||
|
Добавлено через 1 минуту
2
|
|||
|
|
||
| 15.12.2021, 17:02 | ||
Примеров по MVVM + DI не нашел подходящих, или может плохо искал...Добавлено через 3 минуты kotelok, Просто в моём случае я получаю все ссылки в BaseVM, и потом уже во всех VM использую что надо, а тут получится под каждую VM инжектить своё, или как-то можно через BaseVM решить?
0
|
||
|
Фрилансер
761 / 458 / 146
Регистрация: 06.01.2017
Сообщений: 4,856
|
|||
| 15.12.2021, 17:34 | |||
|
но я честно признаюсь что примерно такой подход только в чуть болеее надежном виде иногда применяю когда лениво тянуть полновесные библиотеки а результат нужен уже вчера)) когда проект крупный без этого никак а то такая вермишель для вложенных VM образуется тогда и начинаешь ценить DI))
0
|
|||
|
1341 / 920 / 265
Регистрация: 08.08.2014
Сообщений: 2,775
|
|||||||||||||||||||||
| 15.12.2021, 18:39 [ТС] | |||||||||||||||||||||
|
Andrey-MSK
Простейший вариант. В реальной реализации метод 'Create' (фабрики) содержит больше логики по начальной инициализации VM, например, после конструктора вызывает цепочку виртуальных методов разных этапов инициализации VM, которые могут быть переопределены при реализации VM, и в которых можно достаточно тонко разделить этапы начальной инициализации - предзагрузка справочников и заполнение комбобоксов/списков, загрузка начального состояния из внешнего сервиса, инициализация подписок на события, установка значений по умолчанию. Единственная очевидная проблема при таком подходе - в метод 'Create' (и в конструктор VM) не получится передать какие-то данные для начальной инициализации (вернее можно, но строгую типизацию у меня сделать не получается даже через дженерики). Пока что это обходится через передачу в Create колбэка (анонимного), который вызывается до окончания метода Create, но после конструктора и части встроенной инициализации VM. И там можно для экземмпляра VM вызвать метод 'Load(itemId)' или выполнить любую другую кастомную настройку. Кривовато и надо помнить, но других вариантов пока не нашёл. Сам ServiceProvider нигде внутри VM не используется. Возможно, это уже вопрос заморочек на перфекционизм, но мне лично комфортнее, когда я по конструктору сразу вижу все зависимости. Тесты на VM не делал, но говорят, что и тестировать их попроще в этом случае, т.к. можно просто в конструктор выдать только то, что реально нужно этой VM. P.S.: ошмётки ReactiveUI в коде опциональны, на DI он никак не влияет. Пример VM
ViewModelFactory
Startup приложения
Добавлено через 7 минут Пример вызова расширенного Create с колбэком (тут async-версия)
2
|
|||||||||||||||||||||
|
Модератор
|
||
| 15.12.2021, 21:57 | ||
|
В тонком клиенте обычно вообще нет Модели. Собственно, поэтому он и "тонкий". Иногда некую часть VM, работающую с внешним API, выделяют в "Локальную Модель". Но по сути это просто удобное название для определённой части функционала общего слоя VM.
1
|
||
|
1341 / 920 / 265
Регистрация: 08.08.2014
Сообщений: 2,775
|
||||||||||||||||
| 17.12.2021, 16:10 [ТС] | ||||||||||||||||
|
В общем, предварительный рабочий вариант выглядит следующим образом (по сути то, что Элд Хасп предлагал, но с учётом прочих нюансов/сценариев).
Суть - любые обработчики, которые ... 1. Могут занимать какое-то время (как правило минимальное, но сервер/сеть может и призадуматься). 2. Не должны блокировать UI-поток. 3. Конкурируют за ресурсы VM (т.е. даже два разных обработчика могут конкурировать, модифицируя одну и ту же коллекцию и при этом очень не хочется внутри каждого задумываться про явные блокировки). ... ставятся в общую внутреннюю очередь VM и выполняются последовательно с возможностью скипнуть последовательный блок однотипных обработчиков (skipSame = true). Как оформить это на тасках и 'ContinueWith' разобраться не смог, т.к. для этого нужно слишком глубоко во внутреннее устройство этого механизма вникать. Так что используется запускаемый явным образом поток, так я хотя бы понимаю когда он стартует/завершается и могу там корректно исключение поймать/подавить (если сам обработчик этого не делает). Все обработчики пока что 'async Task', т.к. они могут использоваться и напрямую в некоторых сценариях, где нужен 'await' на них (может потом будут другие варианты и надо будет под них модифицировать). Возможно, кто-нибудь что-нибудь подскажет по улучшению/упрощению/ошибкам этой реализации.
1
|
||||||||||||||||
|
Фрилансер
761 / 458 / 146
Регистрация: 06.01.2017
Сообщений: 4,856
|
|
| 17.12.2021, 17:34 | |
|
пару ссылок по теме
https://stackoverflow.com/ques... -method-is https://stackoverflow.com/ques... ect=1&lq=1 когда у меня была похожая задача я скомбинировал примитивы доступные ReactiveUi примерно так как в одном из ответов из первой ссылки
1
|
|
|
Модератор
|
|||||||||||||||||
| 18.12.2021, 09:49 | |||||||||||||||||
1
|
|||||||||||||||||
|
1182 / 624 / 160
Регистрация: 19.04.2018
Сообщений: 2,923
|
|||||||
| 18.12.2021, 12:28 | |||||||
В методе есть запрос, который нужно awaitить. Результат запроса я обрабатываю тут . В итоге поднимается событие с какими-то результатом. События приходят хаотично (не последовательно). Поэтому в событии я указываю его порядковые номер. Если у меня пришло событие с номером 0, а потом событие с номером 2 — значит событие 1 ещё не дошло. Обрабатываю следующим способом: ссылка доступна только для ЭлдХасп
0
|
|||||||
|
Модератор
|
||
| 18.12.2021, 12:39 | ||
|
В WEB используются штампы времени. Но я реально сталкивался когда с одним штампом приходит несколько событий и в неверной последовательности. Допустим, добавить элемент, изменить элемент. А первым приходит изменить. А элемента ещё нет и кого менять непонятно. Выкинуть тоже нельзя. Так как потом придёт "добавить" и оно будет с неактуальными данными. Приходится вводить приоритеты событий, очередь обработчиков, ожидание в этой очереди и т.д. На мой взгляд, надёжнее и проще номер события.
1
|
||
|
1182 / 624 / 160
Регистрация: 19.04.2018
Сообщений: 2,923
|
|
| 18.12.2021, 13:24 | |
|
Элд Хасп, вот как, хорошо, принял. Спасибо!
0
|
|
| 18.12.2021, 13:24 | |
|
Асинхронные операции Асинхронные операции
Как работают асинхронные операции в Qt Искать еще темы с ответами Или воспользуйтесь поиском по форуму: |
|
Новые блоги и статьи
|
|||
|
Запустил конкурс "тем и промптов для текстовых квестов созданных почти чисто ИИ"
Adler 06.10.2026
Всем привет!
За последние три-четыре дня я создал более 16 текстовых квестовых игр используя преимущественно по одному запросу к ИИ на игру. Мне так понравилось смотреть все ветки/ сцены во всех. . .
|
ИИ не может найти нужный язык в списке
Supersumestria 05.10.2026
Я ему даю вот такое изображение и прошу найти и подчеркнуть немецкий язык.
Возвращает он вот это:
https:/ / i. **********/ vqBWLe2. png
Нужную строчку в 3й колонке просто выдумал. .
Это. . .
|
Новая последняя моя музыка в SUNO
zorxor 05.10.2026
Здравствуйте, дорогие мои друзья! С большой радостью я хотел бы представить вам свою новую последнею музыку, которую сгенерировала мне по моей просьбе нейросеть SUNO. С уважением, zorxor.
Это. . .
|
Nekobox - outbounds[0].transport: unknown transport type: raw
damix 01.10.2026
Фикс ошибки
Правым кликом по серверу -> отладочная информация -> edit
Заменить "net": "raw", на "net": "tcp",
Нажать кнопку reload.
|
|
Программный домашний кинотеатр
russiannick 27.09.2026
Сподобился на программный домашний кинотеатр. В качестве ЯВУ по традиции выбрал js.
В помощники взял Яндекс-Алису.
Было создано три зала на разные интересы.
исторические и ретро
сериал Хичкок. . .
|
Беседа с ИИ о программистах, недопускающих к созданию и правке кода генеративные ИИ и причины этого
zorxor 21.09.2026
Раньше я радовался или получал некоторые эмоции, пусть небольшие, но всё же, от самого процесса написания кода, рекомпиляции и запуска, видя постепенное развитие программы и прочее. А теперь лень. . .
|
Мобильное приложение ColorStep
pavlinmavlin 17.09.2026
Реализовал приложение Красный, Зеленый, Синий в Unity3d + c#.
Название изменил на ColorStep.
Приложение прошло модерацию и теперь доступно для скачивания. Делал его сам, шаг за шагом — и вот,. . .
|
Запрет дублирования строк в табличной части
Maks 13.09.2026
Реализация из решения ниже выполнена на нетиповом справочнике "Нормы ТО" с табличной часть "Виды ТО", разработанного в КА2, со следующими реквизитами:
- ВидТО (СправочникСсылка. ВидыТО);
- ВидГСМ. . .
|