При разработке в 1С мы часто сталкиваемся с необходимостью выполнить сложный и долгий алгоритм обработки данных на сервере. Это может быть закрытие месяца, массовое проведение документов или загрузка большого объема данных. Естественно, нам хочется информировать пользователя о том, что происходит внутри этой процедуры: на каком этапе находится процесс, сколько объектов обработано или какие возникли предупреждения. Давайте разберем по шагам, как правильно реализовать такой вывод сообщений в процессе выполнения длительной операции.
Для начала проанализируем ситуацию, с которой сталкиваются многие программисты. Мы пытаемся вставить метод Сообщить() или использовать объект СообщениеПользователю внутри длинного серверного цикла. Однако вместо ожидаемого появления сообщений по мере выполнения кода, мы видим, что интерфейс «зависает», а все сообщения вываливаются на экран единым списком только после полного завершения процедуры.
Выясним причину этого явления. В архитектуре управляемого приложения 1С клиент и сервер общаются синхронно в рамках одного серверного вызова. Когда мы запускаем процедуру на сервере, клиент полностью блокируется и переходит в режим ожидания ответа. Любые вызванные в процессе функции платформы накапливают текстовые сообщения в специальном буфере на сервере. Эти данные передаются на клиент только в момент возврата управления (когда процедура полностью отработала). Именно поэтому единственный методологически правильный выход для отображения процесса в реальном времени — это использование асинхронности, то есть запуск процесса в отдельном потоке (фоновом задании).
Рассмотрим самый правильный и рекомендуемый вариант для конфигураций, построенных на базе БСП (например, 1С:Управление торговлей 11.4). В БСП уже есть целый готовый «огород» — подсистема «Длительные операции», которая берет на себя всю рутину по управлению фоновыми потоками.
Разберем механику реализации по шагам:
ДлительныеОперацииКлиент, который инициирует серверный метод ДлительныеОперации.ВыполнитьВФоне().ДлительныеОперацииКлиент.ОжидатьЗавершение(). Эта процедура с заданной периодичностью (например, каждые 2 секунды) делает короткие пинги на сервер, чтобы узнать состояние нашего задания.ДлительныеОперации.СообщитьПрогресс(). В него мы передаем процент выполнения и текст текущей операции (например, «Обработан документ 10 из 100»).Также, если вы работаете с чистой платформой без БСП, можно реализовать аналогичный механизм самостоятельно, используя метод ПолучитьСообщенияПользователю() для объекта фонового задания, вызывая его по таймеру на клиенте.
В качестве альтернативы встроенному прогрессу БСП или в самописных конфигурациях, мы посмотрим на пример использования временного хранилища. Это классический и очень надежный паттерн.
Проанализируем этот подход:
Состояние().Если в вашей базе активно используется Система взаимодействия (СВ), рассмотрим еще один современный метод. Система взаимодействия работает по принципу веб-сокетов, что обеспечивает постоянное двустороннее соединение между сервером СВ и клиентом.
Преимущество этого метода в том, что сообщения доставляются пользователю моментально и асинхронно, полностью минуя классический цикл «клиент-серверного вызова».
Посмотрим на пример того, как это работает:
СистемаВзаимодействия.СообщениеСистемыВзаимодействия.В завершение разберем критически важный аспект разработки асинхронных процессов — обработку исключительных ситуаций. Когда мы переносим выполнение кода из обычного серверного вызова в фоновое задание, меняется логика падения системы при ошибках.
Если ваш код внутри фонового задания упадет с необработанной ошибкой (исключением), фоновое задание просто завершится со статусом «Отменено» или «Ошибка». Проблема в том, что платформа «тихо» погасит этот процесс. Пользователь ничего не увидит, окно прогресса просто закроется, и возникнет иллюзия, что операция успешно завершилась (или просто исчезла).
Чтобы избежать этой проблемы, клиентский код, который опрашивает фоновое задание (или встроенный механизм БСП, если вы настраиваете его вручную), должен обязательно проверять статус задания при его завершении. Если статус сигнализирует об ошибке, необходимо получить свойство ИнформацияОбОшибке у объекта фонового задания и явно вывести текст этой ошибки на экран с помощью предупреждения. Только так мы обеспечим прозрачность процесса и понимание ситуации для конечного пользователя.