Как отобразить индикатор прогресса при выполнении длительных операций на сервере 1С?

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

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

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

Почему интерфейс "зависает" и почему нельзя просто обновлять реквизиты?

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

Для решения этой задачи нам необходимо запустить серверный код в отдельном потоке (фоновом задании), а в клиентском сеансе настроить периодический "опрос" состояния этого задания.

Решение через БСП: Подсистема "Длительные операции"

Наиболее современным и надежным способом является использование подсистемы ДлительныеОперации из состава БСП. Рассмотрим алгоритм реализации по шагам.

Шаг 1. Создание серверного метода. Мы должны вынести логику обработки в экспортную процедуру общего модуля (модуль должен иметь флаг "Вызов сервера"). В этой процедуре мы будем использовать метод для передачи прогресса.


Процедура ЗагрузитьДанныеНаСервере(ПараметрыЗагрузки, АдресРезультата) Экспорт
    
    МассивФайлов = ПараметрыЗагрузки.МассивФайлов;
    ВсегоФайлов = МассивФайлов.Количество();
    
    Для Индекс = 0 По ВсегоФайлов - 1 Цикл
        
        // Эмуляция обработки файла
        ТекущийФайл = МассивФайлов[Индекс];
        ОбработатьОдинФайл(ТекущийФайл);
        
        // Сообщаем прогресс
        Процент = Целое((Индекс + 1) / ВсегоФайлов * 100);
        ТекстСообщения = "Обработка файла: " + ТекущийФайл.Имя;
        
        ДлительныеОперации.СообщитьПрогресс(Процент, ТекстСообщения);
        
    КонецЦикла;
    
    // Если нужно вернуть результат, помещаем его во временное хранилище
    РезультатТЗ = СформироватьИтоговуюТаблицу();
    ПоместитьВоВременноеХранилище(РезультатТЗ, АдресРезультата);
    
КонецПроцедуры

Шаг 2. Запуск на клиенте. На форме обработки мы вызываем метод БСП, который возьмет на себя создание фонового задания и открытие формы ожидания.


&НаКлиенте
Процедура ВыполнитьЗагрузку(Команда)
    
    ПараметрыВыполнения = ДлительныеОперацииКлиент.ПараметрыВыполнения(ЭтотОбъект);
    ПараметрыВыполнения.ТекстСообщения = "Пожалуйста, подождите. Идет загрузка данных...";
    ПараметрыВыполнения.ОповещениеОЗавершении = Новый ОповещениеЗавершения("ЗагрузкаЗавершена", ЭтотОбъект);
    
    // Формируем параметры для нашей серверной процедуры
    ПараметрыПроцедуры = Новый Структура;
    ПараметрыПроцедуры.Вставить("МассивФайлов", ПолучитьСписокФайлов());
    
    Операция = ДлительныеОперацииКлиент.ВыполнитьВФоне(
        "МойОбщийМодуль.ЗагрузитьДанныеНаСервере", 
        ПараметрыПроцедуры, 
        ПараметрыВыполнения
    );
    
КонецПроцедуры

Шаг 3. Обработка завершения. После того как фоновое задание закончит работу, сработает процедура оповещения.


&НаКлиенте
Процедура ЗагрузкаЗавершена(Результат, ДополнительныеПараметры) Экспорт
    
    Если Результат = Неопределено Тогда 
        Возврат; // Пользователь отменил операцию
    КонецЕсли;
    
    Если Результат.Статус = "Выполнено" Тогда
        // Получаем данные из хранилища
        ТЗ = ПолучитьИзВременногоХранилища(Результат.АдресРезультата);
        ПоказатьОповещениеПользователя("Успех", , "Загрузка завершена успешно!");
    ИначеЕсли Результат.Статус = "Ошибка" Тогда
        ВызватьИсключение Результат.ПодробноеПредставлениеОшибки;
    КонецЕсли;
    
КонецПроцедуры

Низкоуровневая реализация без БСП

Если ваша конфигурация не содержит БСП, нам придется реализовать механизм вручную через ФоновыеЗадания и ПодключитьОбработчикОжидания. Разберем, как это работает.

Проанализируем ситуацию: нам нужно запустить процедуру на сервере и периодически "спрашивать" сервер из клиента: "Ну что там?". Для передачи информации о прогрессе удобнее всего использовать механизм сообщений пользователю.

1. Запуск фонового задания. При нажатии кнопки на форме мы создаем уникальный идентификатор и запускаем фоновое задание через объект ФоновыеЗадания.


&НаКлиенте
Процедура СтартВручную(Команда)
    
    ИдентификаторЗадания = ЗапуститьЗаданиеНаСервере();
    
    // Подключаем опрос состояния каждые 2 секунды
    ПодключитьОбработчикОжидания("ПроверитьСтатусЗадания", 2);
    
КонецПроцедуры

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


// В серверной процедуре фонового задания
Процент = 50;
ТекстПрогресса = "{""Процент"":" + Процент + ",""Текст"":""Обработано полпути""}";
Сообщить(ТекстПрогресса);

3. Чтение прогресса на клиенте. В обработчике ожидания мы получаем список сообщений, которые "накопило" фоновое задание.


&НаКлиенте
Процедура ПроверитьСтатусЗадания()
    
    Статус = ПолучитьСтатусИСообщения(ИдентификаторЗадания);
    
    // Обновляем индикатор на форме
    Элементы.ИндикаторПрогресса.Значение = Статус.Процент;
    Состояние(Статус.Текст);
    
    Если Статус.Завершено Тогда
        ОтключитьОбработчикОжидания("ПроверитьСтатусЗадания");
        ПоказатьОповещениеПользователя("Готово!");
    КонецЕсли;
    
КонецПроцедуры

Анализ типичных ошибок из практики

Разберем ошибки, с которыми столкнулся автор темы, чтобы не повторять их:

  1. Неправильные кавычки: В коде 1С крайне важно использовать стандартные кавычки (клавиша Shift+'). Использование "типографских" кавычек-елочек (« ») приведет к ошибке компиляции Ожидается выражение. Проверьте раскладку клавиатуры и настройки автозамены в текстовых редакторах.
  2. Доступность типов данных: Автор пытался передать Таблицу Значений напрямую с сервера на клиент. Помните: ТаблицаЗначений — это серверный тип. Для передачи на клиент используйте либо Массив структур, либо ПоместитьВоВременноеХранилище.
  3. Параметр ЗначениеВРеквизитФормы: Ошибка Недопустимое значение параметра (номер '2') возникает, когда в качестве второго параметра передается строка, не совпадающая с именем реквизита на форме, или если тип данных реквизита не соответствует передаваемому значению.
  4. Контекст выполнения: Помните, что ПодключитьОбработчикОжидания работает только в контексте клиента. Попытка вызвать этот метод внутри серверной процедуры (даже если она вызвана с клиента) приведет к ошибке.

Рекомендации по оптимизации

Рассмотрим важные нюансы производительности:

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

← На главную