Как реализовать индикатор прогресса на управляемых формах в 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке на управляемых формах программисты часто сталкиваются с проблемой: выполнение длительной операции на сервере «замораживает» интерфейс пользователя. Пользователь не видит, сколько времени осталось до конца процесса, и может решить, что программа зависла. Попытки вызвать стандартную команду Состояние() внутри серверного цикла не приносят результата, так как этот метод работает исключительно на клиенте. Разберем подробно, как правильно организовать визуализацию процесса выполнения задачи.

Подготовка интерфейса: создание индикатора

Прежде чем приступать к написанию кода, подготовим визуальную часть. В 1С:Предприятие 8.3 для отображения прогресса не нужно использовать сторонние компоненты. Достаточно стандартных средств платформы (хотя в некоторых случаях может потребоваться индикатор прогресса в ячейке динамического списка). Проанализируем шаги по настройке формы:

  1. Создадим на форме новый реквизит, назовем его ПрогрессВыполнения. Установим тип данных — Число (длина 3, точность 0).
  2. Перетащим этот реквизит в дерево элементов формы.
  3. В свойствах созданного элемента (поля формы) установим вид — Поле индикатора.
  4. В параметрах поля укажем: Минимум — 0, Максимум — 100.

Теперь, когда мы будем менять значение реквизита ПрогрессВыполнения на клиенте, полоска индикатора будет автоматически заполняться.

Способ 1. Порционное выполнение (клиент-серверный цикл)

Этот метод является самым простым для понимания и реализации, если вы не хотите использовать фоновые задания. Идея заключается в том, чтобы разделить одну большую задачу на много маленьких порций. Рассмотрим этот процесс по шагам:

Сначала подготовим данные на сервере (например, соберем массив ссылок объектов, которые нужно обработать) и вернем их на клиент. Затем на клиенте запустим цикл, который будет вызывать сервер для обработки каждой порции отдельно.


&НаКлиенте
Процедура ВыполнитьОбработку(Команда)
    
    // Получаем массив объектов для обработки
    ДанныеДляОбработки = ПодготовитьДанныеНаСервере();
    ВсегоОбъектов = ДанныеДляОбработки.Количество();
    
    Если ВсегоОбъектов = 0 Тогда
        Возврат;
    КонецЕсли;
    
    // Настраиваем начальные значения индикатора
    Объект.ПрогрессВыполнения = 0;
    
    // Запускаем цикл обработки порциями
    Для Итерация = 0 По ВсегоОбъектов - 1 Цикл
        
        ОбработатьОбъектНаСервере(ДанныеДляОбработки[Итерация]);
        
        // Обновляем индикатор
        Объект.ПрогрессВыполнения = (Итерация + 1) / ВсегоОбъектов * 100;
        
        // Принудительно обновляем интерфейс (для тонкого клиента)
        ОбработкаПрерыванияПользователя();
        
    КонецЦикла;
    
    ПоказатьПредупреждение(, "Обработка завершена!");
    
КонецПроцедуры

Важный нюанс: Этот способ хорошо работает в файловых базах, но в клиент-серверном варианте частые вызовы сервера (ping-pong между клиентом и сервером) могут замедлить общую скорость выполнения. Поэтому рекомендуется обрабатывать объекты не по одному, а пачками (например, по 100 штук за один вызов сервера).

Способ 2. Использование фоновых заданий и временного хранилища

Для более тяжелых операций правильнее использовать Фоновые задания. Это позволяет серверу выполнять задачу в отдельном потоке, а клиенту лишь периодически «спрашивать» сервер о текущем статусе. Выясним, как передать прогресс из фонового задания на форму.

Поскольку фоновое задание не имеет доступа к форме, мы будем использовать ПоместитьВоВременноеХранилище(). Разберем алгоритм:

  1. На клиенте генерируем уникальный идентификатор (УИД) и запускаем фоновое задание.
  2. Внутри серверной процедуры фонового задания на каждой итерации помещаем структуру с текущим процентом в хранилище по этому УИД.
  3. На клиенте подключаем обработчик ожидания, который раз в 1-2 секунды читает данные из хранилища.

Пример кода для серверной части (в модуле объекта или общем модуле):


Процедура ДлительнаяОперацияНаСервере(АдресХранилища, Параметры) Экспорт
    
    Выборка = СформироватьВыборку(Параметры);
    Всего = Выборка.Количество();
    Счетчик = 0;
    
    Пока Выборка.Следующий() Цикл
        // Основная логика обработки
        ОбработатьДанные(Выборка.Ссылка);
        
        Счетчик = Счетчик + 1;
        Процент = (Счетчик / Всего) * 100;
        
        // Передаем прогресс через хранилище
        ПоместитьВоВременноеХранилище(Процент, АдресХранилища);
    КонецЦикла;
    
КонецПроцедуры

На стороне клиента нам понадобится метод ПодключитьОбработчикОжидания(), который будет обращаться к функции, проверяющей состояние хранилища и обновляющей реквизит Объект.ПрогрессВыполнения.

Способ 3. Использование БСП (Библиотека стандартных подсистем)

Если ваша конфигурация построена на базе БСП (что актуально для большинства современных решений, таких как УТ 11, БП 3.0, ERP), мы крайне рекомендуем использовать стандартную подсистему «Длительные операции». Это наиболее стабильный и «красивый» способ.

Проанализируем работу с общим модулем ДлительныеОперации. Для этого в серверном коде, который выполняется в фоне, необходимо вызывать метод СообщитьПрогресс():


// Код на сервере внутри фонового задания
Процедура ВыполнитьДействие() Экспорт
    // ... какой-то цикл ...
    ДлительныеОперации.СообщитьПрогресс(ТекущийПроцент, "Обработка документов...");
КонецПроцедуры

На клиенте запуск и отслеживание выглядят очень лаконично благодаря методу ОжидатьЗавершение(). Система сама откроет стандартное окно с индикатором прогресса и кнопкой отмены, что соответствует стандартам интерфейса 1С.

Почему Состояние() не работает на сервере?

Разберем типичную ошибку начинающих разработчиков. Метод Состояние() управляет нижней строкой статуса в окне приложения. Когда код уходит на сервер, он выполняется в контексте рабочего процесса (rphost), у которого нет «экрана». Вся информация, которую сервер хочет передать пользователю, должна быть либо возвращена как результат функции, либо записана в доступное обоим контекстам место (база данных, временное хранилище, сообщения пользователю). Помните, что интерфейс — это прерогатива клиента.

Советы по оптимизации

Рассмотрим несколько практических советов для эффективной работы индикатора:

Таким образом, выбор метода зависит от архитектуры вашей задачи. Для простых внешних обработок подойдет порционный метод, для серьезных механизмов внутри конфигурации — БСП или самописные фоновые задания через временное хранилище.

← На главную