Многие разработчики при переходе с обычных форм на управляемые сталкиваются с неприятной ситуацией: при запуске длительного цикла обработки данных (например, пересчет цен или изменение реквизитов номенклатуры) интерфейс программы "замирает". Вместо привычного обновления строк в окне сообщений пользователь видит вращающийся кружочек курсора, а все сообщения Сообщить() "вываливаются" на экран только после завершения всей процедуры. Разберем подробно, почему это происходит и как заставить 1С показывать прогресс в реальном времени.
Проанализируем архитектуру управляемого приложения. В отличие от обычных форм, где клиент и сервер часто были тесно связаны в рамках одного процесса, в управляемом приложении разделение жесткое. Когда вы вызываете процедуру с директивой &НаСервере, управление передается на сервер, а клиентский поток переходит в режим ожидания. Пока сервер не завершит выполнение всей процедуры и не вернет управление клиенту, пакет данных с накопленными сообщениями не будет передан. Именно поэтому вы видите результат работы Сообщить() только в самом конце.
Рассмотрим основные способы решения этой задачи, от стандартных механизмов БСП до современных методов с использованием Системы взаимодействия.
Если ваша конфигурация построена на базе Библиотеки стандартных подсистем (БСП), это самый правильный и надежный путь. Механизм позволяет запустить тяжелую обработку в фоновом задании, не блокируя основной поток интерфейса. Разберем по шагам, как это работает.
Во-первых, процедура обработки данных выносится в серверный модуль и помечается как экспортная. Во-вторых, на клиенте вызывается метод БСП, который запускает эту процедуру в фоне. Проанализируем примерную схему кода:
// Код на клиенте для запуска
ПараметрыВыполнения = ДлительныеОперации.ПараметрыВыполненияПроцедуры(УникальныйИдентификатор);
ПараметрыВыполнения.НаименованиеФоновогоЗадания = "Обработка номенклатуры";
Результат = ДлительныеОперации.ЗапуститьПроцедуруНаСервере(
"МойСерверныйМодуль.ОбработатьДанные",
ПараметрыПроцедуры,
ПараметрыВыполнения
);
После запуска клиентская форма не "зависает", а открывает небольшое окно ожидания. БСП автоматически опрашивает сервер о состоянии задания. Чтобы передать текст сообщения или процент выполнения, внутри серверного цикла мы можем использовать метод Сообщить() или записывать данные в структуру прогресса. БСП перехватит эти сообщения и отобразит их пользователю в процессе выполнения. Для практической реализации можно использовать примеры кода длительных операций в БСП.
Если БСП не используется, мы можем реализовать аналогичный механизм самостоятельно. Основная идея заключается в том, что клиент должен периодически "спрашивать" сервер: "Как дела?".
Рассмотрим алгоритм реализации:
ПодключитьОбработчикОжидания(), который раз в 1-2 секунды вызывает серверную функцию для чтения данных из хранилища.Посмотрим на пример кода серверной части:
Процедура ОбработатьДанныеФоново(АдресХранилища) Экспорт
Выборка = Справочники.Номенклатура.Выбрать();
Пока Выборка.Следующий() Цикл
// Основная работа
Объект = Выборка.ПолучитьОбъект();
// ... изменения ...
Объект.Записать();
// Передаем статус выполнения
ТекстСтатуса = "Обработано: " + Объект.Наименование;
ПоместитьВоВременноеХранилище(ТекстСтатуса, АдресХранилища);
// Позволяет прервать задание администратором или системой
ОбработкаПрерыванияПользователя();
КонецЦикла;
КонецПроцедуры
На клиенте мы будем считывать ТекстСтатуса и выводить его в надпись на форме. Это создаст эффект "живого" интерфейса без блокировки программы. Для такой задачи также можно задействовать пример фоновых заданий с индикацией прогресса без БСП.
Это наиболее современный подход, доступный в новых версиях платформы 8.3. Выясним причину его эффективности: сервер сам может "толкнуть" сообщение клиенту, не дожидаясь запроса от него. Это избавляет от необходимости использовать обработчики ожидания.
Для реализации этого метода нам потребуется настроенная Система взаимодействия. В цикле на сервере мы отправляем сообщение в определенный канал или конкретному пользователю. Клиентское приложение, подписанное на это событие, мгновенно получает текст и обновляет информацию на экране. Это максимально производительный способ, так как он минимизирует количество бессмысленных обращений клиента к серверу.
В обсуждении часто всплывает совет: "Запустите цикл на клиенте и вызывайте сервер для каждого объекта". Выясним, почему это плохой вариант:
Важно различать Сообщить() и Состояние(). Метод Состояние() предназначен для вывода информации в строку состояния (внизу экрана) и управления индикатором прогресса (Progress Bar). Однако он работает только на стороне клиента. Если вы вызовете Состояние() на сервере, платформа попытается передать это на клиент, но опять же — только после завершения всего серверного вызова. Поэтому для визуализации прогресса лучше всего комбинировать фоновые задания и обновление элемента Индикатор на форме.
Подведем итог: для реализации отзывчивого интерфейса при длительных операциях в 1С 8.3 следует уходить от линейного выполнения кода в основном потоке. Используйте ДлительныеОперации из состава БСП или связку фоновых заданий с временным хранилищем. Это позволит вашим пользователям видеть актуальную информацию о ходе процесса, не дожидаясь "отвисания" программы.