При переходе с обычных форм на управляемые многие разработчики сталкиваются с проблемой: при запуске длительной серверной операции, например, обработки большого списка документов или контрагентов, интерфейс пользователя полностью «зависает». Нельзя нажать ни на одну кнопку, а прогресс выполнения не виден до самого конца. Это происходит из-за архитектурного разделения клиента и сервера. Пока сервер выполняет один большой вызов, он не может вернуть управление клиенту, чтобы тот обновил данные на экране.
Рассмотрим, почему так происходит и какие существуют способы организовать интерактивное обновление формы, чтобы пользователь видел прогресс и мог продолжать работу с программой.
В архитектуре управляемых форм клиент (интерфейс) и сервер (обработка данных) работают раздельно. Когда вы с клиентской формы вызываете серверную процедуру, вызов является модальным. Это означает, что клиент будет ждать, пока сервер полностью не завершит свою работу. Если на сервере запущен цикл по обработке тысячи контрагентов, клиент будет «заморожен» всё это время. Платформа не позволяет серверному коду напрямую управлять элементами формы на клиенте в середине своего выполнения.
Чтобы обойти это ограничение, необходимо изменить подход к разработке. Вместо одного долгого вызова нужно использовать механизмы, которые позволяют выполнять код асинхронно или порционно. Разберем три основных подхода: от самого простого до наиболее продвинутого.
Это самый доступный для понимания и отладки метод, идеально подходящий для несложных задач. Его суть — разбить одну большую операцию на множество маленьких, независимых частей (порций). После обработки каждой порции управление возвращается на клиент, интерфейс обновляется, а затем запускается обработка следующей порции.
Рассмотрим по шагам алгоритм реализации:
&НаСервере
Процедура ОбработатьПорциюКонтрагентов(ПорцияДанных)
Для каждого Элемент Из ПорцияДанных Цикл
// Получаем объект, изменяем, записываем
ОбъектКонтрагента = Элемент.ПолучитьОбъект();
ОбъектКонтрагента.Комментарий = "Обработан " + ТекущаяДата();
ОбъектКонтрагента.Записать();
КонецЦикла;
КонецПроцедуры
ПодключитьОбработчикОжидания() позволяет вызвать определенную клиентскую процедуру через заданный интервал времени (например, 0.1 секунды) всего один раз. В этой процедуре мы будем проверять, остались ли еще данные для обработки.
&НаКлиенте
Процедура ВыполнитьОбработку()
// ... здесь код подготовки массива данных для обработки ...
// Запускаем первый шаг
ОбработатьСледующуюПорцию();
КонецПроцедуры
&НаКлиенте
Процедура ОбработатьСледующуюПорцию()
// Определяем, есть ли еще что обрабатывать
Если ДанныеДляОбработки.Количество() > 0 Тогда
// Берем порцию, например, 10 элементов
Порция = Новый Массив;
Для сч = 1 по Мин(10, ДанныеДляОбработки.Количество()) Цикл
Порция.Добавить(ДанныеДляОбработки[0]);
ДанныеДляОбработки.Удалить(0);
КонецЦикла;
// Вызываем сервер для обработки порции
ОбработатьПорциюКонтрагентов(Порция);
// Обновляем элементы формы, например, список
Элементы.СписокКонтрагентов.Обновить();
// Планируем вызов следующей порции
ПодключитьОбработчикОжидания("ОбработатьСледующуюПорцию", 0.1, Истина);
Иначе
Сообщить("Обработка завершена!");
КонецЕсли;
КонецПроцедуры
Важный момент: в обработчике ожидания третий параметр (Однократно) установлен в Истина. Это значит, что после выполнения он сам отключится. Мы снова его подключаем внутри процедуры, пока есть что обрабатывать. Если бы мы хотели постоянный вызов, мы бы подключили его один раз в начале с параметром Ложь, а в конце вызвали бы ОтключитьОбработчикОжидания().
Плюсы метода:
Минусы метода:
Это стандартный и наиболее правильный с точки зрения архитектуры 1С подход для реализации длительных операций. Он предполагает запуск обработки в отдельном сеансе — фоновом задании. Пользовательский интерфейс при этом не блокируется, и пользователь может продолжать работать в программе.
Вручную управлять фоновыми заданиями довольно сложно. К счастью, в Библиотеке стандартных подсистем (БСП) есть готовый механизм ДлительныеОперации, который сильно упрощает этот процесс.
Общая схема работы с ДлительныеОперации:
ДлительныеОперации.СообщитьПрогресс(), чтобы обновлять статус выполнения.
ДлительныеОперации.ВыполнитьВФоне(). В нее передается имя процедуры для выполнения и параметры. Функция запускает фоновое задание и возвращает структуру, содержащую адрес результата и идентификатор задания.
ПодключитьОбработчикОжидания запускается периодический опрос состояния фонового задания. Для этого используется функция ДлительныеОперации.ПолучитьСтатус(). Когда задание завершено, обработчик ожидания получает результат, отключается и обновляет данные на форме. БСП также предоставляет готовую форму отображения состояния, которую можно использовать.
Этот механизм сложнее в освоении, особенно для новичков. Отладка фоновых заданий — отдельная непростая задача. Кроме того, при работе с внешними обработками возникают дополнительные сложности, так как контекст внешней обработки нужно передавать в фоновое задание, что требует дополнительных ухищрений (например, сохранение обработки во временное хранилище).
Плюсы метода:
Минусы метода:
Более новый и гибкий механизм, который появился в последних версиях платформы. Система взаимодействия — это, по сути, встроенный мессенджер, который позволяет разным сеансам 1С обмениваться сообщениями.
Сценарий использования такой:
Этот подход реализует механизм коллбэков (callbacks), когда сервер сам инициирует обновление на клиенте, а не клиент постоянно опрашивает сервер. Это наиболее эффективно, но и наиболее сложно в настройке, так как требует развернутого и настроенного сервера взаимодействия.
Плюсы метода:
Минусы метода:
Выбор метода зависит от сложности задачи и вашего опыта:
Проблема «зависания» интерфейса — это не «корявая реализация» платформы, а следствие ее архитектуры. Понимание этой архитектуры и доступных асинхронных механизмов — ключ к созданию удобных и отзывчивых приложений на управляемых формах.