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

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

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

Разберем по шагам, как правильно перевести тяжелые алгоритмы на асинхронную модель выполнения, передать параметры из формы в модуль объекта, отображать прогресс выполнения и корректно работать с внешними обработками.

Архитектура выполнения длительных операций

Для организации непрерывного взаимодействия клиента и сервера длительный процесс разделяется на следующие составляющие:

  1. Фоновое задание на сервере: тяжелая процедура запускается в отдельном серверном потоке через платформенный механизм фоновых заданий или функции подсистемы БСП ДлительныеОперации.
  2. Оповещение о прогрессе: серверный поток в цикле сообщает текущее состояние (процент выполнения, поясняющий текст).
  3. Обработчик ожидания на клиенте: клиентская форма периодически опрашивает состояние задания, считывает накопленный прогресс и обновляет индикаторы на форме.
  4. Управление отменой: клиент инициирует прерывание потока при нажатии кнопки пользователем.

Передача параметров и контекста формы в фоновое задание

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

Посмотрим на пример подготовки данных на клиенте и запуска процедуры через БСП:


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

    ПараметрыВыполнения = Новый Структура;
    ПараметрыВыполнения.Вставить("ИмяМетода", "ВыполнитьДлительнуюОперацию");
    ПараметрыВыполнения.Вставить("ПараметрыВыполнения", ПараметрыПроцесса);

    Результат = ДлительныеОперации.ЗапуститьВыполнениеВФоне(
        УникальныйИдентификатор,
        "ДлительныеОперации.ВыполнитьПроцедуруМодуляОбъектаОбработки",
        ПараметрыВыполнения,
        НСтр("ru = 'Выполняется обработка данных...'")
    );

    // Подключаем обработчик ожидания для проверки статуса
    ПараметрыОжидания = Новый Структура("ИдентификаторЗадания, АдресХранилища", Результат.ИдентификаторЗадания, Результат.АдресХранилища);
    ПодключитьОбработчикОжидания("ПроверитьЗавершениеОбработки", 1, Истина);
КонецПроцедуры

В модуле объекта внешней обработки или конфигурации принимаем переданную структуру и распаковываем ее реквизиты:


Процедура ВыполнитьДлительнуюОперацию(ПараметрыВыполнения, АдресХранилища) Экспорт
    // Восстанавливаем переданные параметры
    Организация = ПараметрыВыполнения.Организация;
    ТаблицаЭлементов = ПараметрыВыполнения.ТаблицаЭлементов;

    ВсегоЭлементов = ТаблицаЭлементов.Количество();
    Для Индекс = 0 По ВсегоЭлементов - 1 Цикл
        ТекущаяСтрока = ТаблицаЭлементов[Индекс];

        // Выполняем полезную работу
        ОбработатьСтроку(ТекущаяСтрока);

        // Информируем о прогрессе выполнения
        Процент = Окр(((Индекс + 1) / ВсегоЭлементов) * 100);
        ТекстСообщения = СтрШаблон("Обработано %1 из %2", Индекс + 1, ВсегоЭлементов);
        
        ДлительныеОперации.СообщитьПрогресс(Процент, ТекстСообщения);
    КонецЦикла;
КонецПроцедуры

Чтение прогресса и отображение на клиенте

В процедуре ожидания на клиенте проверяем статус фонового задания и обновляем интерфейс:


&НаКлиенте
Процедура ПроверитьЗавершениеОбработки()
    Прогресс = ДлительныеОперации.ПрочитатьПрогресс(ПараметрыОжидания.ИдентификаторЗадания);
    Если Прогресс <> Неопределено Тогда
        ИндикаторПрогресса = Прогресс.Процент;
        ТекстСостояния = Прогресс.Текст;
    КонецЕсли;

    ЗаданиеВыполнено = ДлительныеОперации.ЗаданиеВыполнено(ПараметрыОжидания.ИдентификаторЗадания);
    Если ЗаданиеВыполнено Тогда
        ПоказатьОповещениеПользователя("Успешно", , "Обработка данных завершена.");
    Иначе
        ПодключитьОбработчикОжидания("ПроверитьЗавершениеОбработки", 1, Истина);
    КонецЕсли;
КонецПроцедуры

Организация прерывания процесса

Существует два варианта прерывания фонового процесса:

  1. Принудительное прерывание потока: вызывается метод БСП ДлительныеОперации.ОтменитьВыполнениеВФоне(ИдентификаторЗадания) или платформенный метод ФоновыеЗадания.Отменить(ИдентификаторЗадания). В этом случае поток немедленно завершается платформой, а все незафиксированные транзакции откатываются.
  2. Мягкая отмена с сохранением позиции: клиент записывает флаг отмены (например, в регистр сведений или общее хранилище), а серверный цикл периодически проверяет его наличие. При обнаружении флага сервер сохраняет индекс последнего обработанного элемента и штатно завершает работу, что позволяет возобновить расчет позже.

Ошибка при вызове «Новый ДвоичныеДанные» для внешних обработок из справочника

Частая ошибка при переносе кода из локального файла в справочник «Дополнительные отчеты и обработки»:


Ошибка при вызове конструктора (ДвоичныеДанные)
Новый ДвоичныеДанные(МестонахождениеВнешнейОбработкиКлиент);
по причине: Каталог не обнаружен 'e1cib\tempstorage\...'

Причина ошибки: конструктор Новый ДвоичныеДанные(ПутьКФайлу) ожидает путь к реальному файлу на локальном диске. Когда внешняя обработка открыта как файл через проводник, свойство ИспользуемоеИмяФайла возвращает путь на диске. Но когда обработка подключена через подсистему дополнительных отчетов и обработок, платформа помещает ее во временное хранилище, и ИспользуемоеИмяФайла возвращает навигационную ссылку e1cib/tempstorage/....

Решение: нет необходимости считывать навигационную ссылку через конструктор двоичных данных или сохранять ее во временный файл на диске сервера. Метод ПолучитьИзВременногоХранилища() сразу возвращает готовый объект ДвоичныеДанные, а метод подключения внешних обработок умеет принимать адрес хранилища напрямую:


&НаСервере
Функция ПодключитьОбработкуПоАдресу(АдресХранилища)
    // Подключение обработки напрямую из временного хранилища без сохранения на диск
    ИмяОбработки = ВнешниеОбработки.Подключить(АдресХранилища, , Ложь);
    Возврат ВнешниеОбработки.Создать(ИмяОбработки);
КонецФункции

Если в конфигурации используется БСП, для подключения зарегистрированной дополнительной обработки рекомендуется использовать штатный метод:


ИмяОбработки = ДополнительныеОтчетыИОбработки.ПодключитьВнешнююОбработку(СсылкаНаСправочникДопОбработок);
ОбработкаОбъект = ВнешниеОбработки.Создать(ИмяОбработки);

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

← На главную