Разработка в среде 1С:Предприятие часто ставит перед программистом задачу: как показать пользователю, что программа не «зависла», а выполняет сложную обработку данных? Типичная ситуация — попытка открыть форму с анимированным GIF-файлом перед вызовом серверной процедуры. Однако на практике анимация часто замирает, превращаясь в статичную картинку. В этой статье мы подробно разберем, почему это происходит, и рассмотрим несколько эффективных способов решения проблемы.
Прежде всего, проанализируем техническую сторону вопроса. В 1С:Предприятии при выполнении кода в контексте &НаСервере основной поток клиентского приложения блокируется. Это означает, что клиент «ждет» ответа от сервера и не может тратить ресурсы на перерисовку графического интерфейса. Поскольку анимация GIF или работа декорации формы требует периодического обновления кадров силами клиентского приложения, выполнение любого тяжелого серверного кода приводит к визуальной «заморозке» клиента.
Многие начинающие разработчики пытаются использовать метод Состояние(). Рассмотрим его особенности: хотя он позволяет вывести текст в нижнюю панель, при длительном серверном вызове без возврата управления на клиент надпись может либо не появиться вовремя, либо исчезнуть сразу после завершения, не давая пользователю ощущения непрерывного процесса.
Для того чтобы интерфейс оставался «живым», а анимация продолжала проигрываться, необходимо освободить основной поток клиента. Единственный надежный способ сделать это — запустить длительный процесс в фоновом задании. Рассмотрим алгоритм реализации по шагам.
Шаг 1. Вынос логики в общий модуль. Код, который выполняет основную работу, должен быть размещен в серверном общем модуле (экспортная процедура).
// В общем модуле (например, ДлительныеОперацииСервер)
Процедура ВыполнитьТяжелуюОбработку(Параметры, АдресХранилища) Экспорт
// Здесь ваша логика
// Результат можно поместить во временное хранилище
ПоместитьВоВременноеХранилище(РезультатВычислений, АдресХранилища);
КонецПроцедуры
Шаг 2. Запуск задания из формы. На клиенте мы открываем вспомогательную форму с анимацией и запускаем фоновое задание.
&НаКлиенте
Процедура ЗапуститьПроцесс(Команда)
АдресРезультата = ПоместитьВоВременноеХранилище(Неопределено, УникальныйИдентификатор);
ПараметрыПроцедуры = Новый Массив;
ПараметрыПроцедуры.Добавить(НужныеДанные);
ПараметрыПроцедуры.Добавить(АдресРезультата);
// Запускаем фоновое задание
Задание = ФоновыеЗадания.Выполнить("ДлительныеОперацииСервер.ВыполнитьТяжелуюОбработку", ПараметрыПроцедуры);
ИдентификаторЗадания = Задание.УникальныйИдентификатор;
// Открываем форму индикации
ОткрытьФорму("ОбщаяФорма.ИндикаторОжидания");
// Подключаем обработчик ожидания для проверки статуса
ПодключитьОбработчикОжидания("ПроверитьСтатусЗадания", 1); // Проверка каждую секунду
КонецПроцедуры
Шаг 3. Обработчик ожидания. Проанализируем ситуацию: клиент каждую секунду «просыпается», проверяет, не завершилось ли задание на сервере, и при этом анимация в открытой форме продолжает двигаться, так как поток управления регулярно возвращается к интерфейсу.
&НаКлиенте
Процедура ПроверитьСтатусЗадания()
Если ЗаданиеЗавершено(ИдентификаторЗадания) Тогда
ОтключитьОбработчикОжидания("ПроверитьСтатусЗадания");
// Закрываем форму индикатора и получаем результат
ПолучитьРезультатИзХранилища(АдресРезультата);
КонецЕсли;
КонецПроцедуры
Если задача позволяет разделить данные на части (например, обработка 10 000 строк документа), мы можем использовать цикл на клиенте. Разберем этот пример подробнее, так как он позволяет использовать стандартный ПрогрессБар и метод Состояние() эффективно.
Суть метода заключается в том, что мы не отправляем все данные на сервер одним «куском», а делаем это итерационно:
Для Каждого передаем по одному (или по 10-50) элементу в серверную процедуру &НаСервереБезКонтекста.ОбработкаПрерыванияПользователя().Посмотрим на пример кода:
&НаКлиенте
Процедура ВыполнитьОбработкуПорциями(Команда)
МассивДанных = ПолучитьДанныеДляОбработкиНаСервере();
Всего = МассивДанных.Количество();
Счетчик = 0;
Для Каждого Элемент Из МассивДанных Цикл
Счетчик = Счетчик + 1;
Процент = Цел(Счетчик / Всего * 100);
// Обновляем состояние — здесь анимация и прогресс будут работать!
Состояние("Обработка данных...", Процент, "Выполнено: " + Процент + "%", БиблиотекаКартинок.ДлительнаяОперация48);
// Вызываем сервер для конкретной порции
ОбработатьЭлементНаСервере(Элемент);
// Позволяет системе обработать нажатие Ctrl+Break и обновить интерфейс
ОбработкаПрерыванияПользователя();
КонецЦикла;
КонецПроцедуры
Важный нюанс: при использовании этого метода учитывайте накладные расходы на каждый вызов сервера. Если обработка одного элемента занимает миллисекунды, лучше группировать их в пакеты по 100–500 штук, чтобы минимизировать задержки сетевого взаимодействия.
Если ваша конфигурация разработана на основе Библиотеки Стандартных Подсистем (БСП), то изобретать велосипед не нужно. В составе БСП есть подсистема «Длительные операции», которая берет на себя всю рутину по управлению фоновыми заданиями и отображению форм ожидания.
Рассмотрим, как это выглядит в коде:
// На клиенте
ПараметрыВыполнения = ДлительныеОперацииКлиент.ПараметрыВыполненияПроцедуры(ЭтаФорма);
ПараметрыВыполнения.ТекстСообщения = "Пожалуйста, подождите, идет расчет...";
Результат = ДлительныеОперацииКлиент.ВыполнитьПроцедуру(
"МойОбщийМодуль.ДлительнаяПроцедура",
ПараметрыПроцедуры,
ПараметрыВыполнения
);
Метод ДлительныеОперацииКлиент.ВыполнитьПроцедуру автоматически откроет стандартное окно с анимированным «кольцом» или «котиком» (как в БП 3.0), запустит фоновое задание и закроет окно по завершении.
Проанализируем несколько неочевидных моментов, которые часто мешают корректному отображению анимации:
GIF-картинку, проверьте свойство Автовоспроизведение.Поле HTML документа. В него можно вставить SVG-анимацию или CSS-spinner. Преимущество в том, что движок рендеринга HTML (обычно это WebKit или Internet Explorer в зависимости от режима) может продолжать анимацию даже при кратковременных подвисаниях основного потока 1С.Блокировать весь интерфейс или Блокировать окно владельца. Это предотвратит случайные клики пользователя по кнопкам основной формы, пока идет процесс.ОткрытьФорму всегда передавайте уникальный ключ (например, строку или Новый УникальныйИдентификатор).Подводя итог, можно сказать: если вам нужна простейшая анимация «на скорую руку» без фоновых заданий — используйте цикл с порционной обработкой и методом Состояние(). Если же вы строите надежное решение для тяжелых расчетов — ваш выбор фоновые задания или механизмы БСП. Это обеспечит максимальный комфорт пользователю и предотвратит ложные жалобы на «зависшую» программу.