При разработке функционала в 1С часто возникают ситуации, когда выполнение определенной серверной процедуры занимает значительное время. Это может быть связано с обработкой больших объемов данных, сложными вычислениями или взаимодействием с внешними системами. В таких случаях пользовательский интерфейс 1С может казаться "зависшим", что вызывает дискомфорт и непонимание со стороны пользователей. Чтобы избежать этого, крайне важно информировать пользователя о текущем статусе выполнения операции. Особенно актуальна эта задача для внешних обработок, которые по своей природе должны быть гибкими и не требовать изменений основной конфигурации.
Рассмотрим, как мы можем реализовать отображение прогресса выполнения длительной серверной процедуры во внешней обработке 1С, причем с особым вниманием к сценариям, где изменение конфигурации нежелательно или невозможно.
Первым и наиболее очевидным шагом для решения проблемы длительных операций является вынесение их выполнения в фоновые задания. Фоновые задания позволяют асинхронно выполнять код на сервере, не блокируя при этом работу клиентского приложения. Это означает, что пользователь может продолжать работать с программой, пока на заднем плане выполняется ресурсоемкая операция. Однако, чтобы пользователь знал о ходе выполнения, нам потребуется механизм для отслеживания прогресса этого фонового задания и его отображения на клиентской стороне.
Процесс организации выполнения в фоновом задании включает следующие шаги:
ФоновыеЗаданияМенеджер.Рассмотрим общий принцип запуска фонового задания:
// На сервере, в общем модуле или модуле объекта, доступном для фонового задания
Процедура ВыполнитьДлительнуюОперацию(ИдентификаторОбработки, АдресВХранилище, Параметры) Экспорт
// Создаем объект внешней обработки из временного хранилища
ОбработкаОбъект = ВнешниеОбработки.Создать(АдресВХранилище);
// Устанавливаем параметры, если необходимо
Для Каждого КлючЗначение Из Параметры Цикл
ОбработкаОбъект[КлючЗначение.Ключ] = КлючЗначение.Значение;
КонецЦикла;
// Вызываем метод длительной операции в контексте обработки
ОбработкаОбъект.ДлительнаяОперацияСПрогрессом();
КонецПроцедуры
// В модуле объекта внешней обработки, экспортная процедура
Процедура ДлительнаяОперацияСПрогрессом() Экспорт
ОбщееКоличествоШагов = 1000;
Для Шаг = 1 По ОбщееКоличествоШагов Цикл
// Выполняем часть работы
// ...
// Сообщаем о прогрессе
ПроцентВыполнения = Цел(Шаг / ОбщееКоличествоШагов * 100);
СообщитьПрогресс(ПроцентВыполнения, "Выполняется шаг " + Шаг + " из " + ОбщееКоличествоШагов);
// Даем системе возможность обновить состояние фонового задания
Если Шаг % 10 = 0 Тогда // Например, обновляем каждый 10-й шаг
ДлительныеОперацииКлиентСервер.СообщитьПрогресс(ПроцентВыполнения); // Использование БСП, если она есть
КонецЕсли;
КонецЦикла;
КонецПроцедуры
// Пример клиентского кода для запуска
Процедура ЗапуститьОперациюНаСервере()
// 1. Помещаем внешнюю обработку во временное хранилище, чтобы она была доступна на сервере
ДвоичныеДанныеОбработки = Новый ДвоичныеДанные(ИмяФайлаОбработки);
АдресВоВременномХранилище = ПоместитьВоВременноеХранилище(ДвоичныеДанныеОбработки, УникальныйИдентификатор);
// 2. Формируем параметры для фонового задания
ПараметрыФоновогоЗадания = Новый Структура;
ПараметрыФоновогоЗадания.Вставить("АдресВХранилище", АдресВоВременномХранилище);
ПараметрыФоновогоЗадания.Вставить("ИдентификаторОбработки", УникальныйИдентификатор);
ПараметрыФоновогоЗадания.Вставить("Параметры", Новый Структура("Ключ", "ЗначениеПоУмолчанию")); // Ваши параметры для обработки
// 3. Запускаем фоновое задание
ФоновоеЗадание = ФоновыеЗадания.Выполнить("ОбщийМодульПрослойка.ВыполнитьДлительнуюОперацию", ПараметрыФоновогоЗадания, , "Длительная операция");
// 4. Запускаем обработчик ожидания для проверки статуса
ПодключитьОбработчикОжидания("ПроверитьСтатусФоновогоЗадания", 1, Истина);
ЗапомнитьИдентификаторФоновогоЗадания(ФоновоеЗадание.УникальныйИдентификатор);
КонецПроцедуры
// Клиентский обработчик ожидания
Процедура ПроверитьСтатусФоновогоЗадания()
ИдентификаторФоновогоЗадания = ПолучитьЗапомненныйИдентификаторФоновогоЗадания();
Если ИдентификаторФоновогоЗадания = Неопределено Тогда
ОтключитьОбработчикОжидания("ПроверитьСтатусФоновогоЗадания");
Возврат;
КонецЕсли;
СостояниеЗадания = ФоновыеЗадания.Состояние(ИдентификаторФоновогоЗадания);
// Получить прогресс через сообщения фонового задания
// Или, если используем БСП, через ДлительныеОперацииКлиентСервер.ПрочитатьПрогресс()
Если СостояниеЗадания = СостояниеФоновогоЗадания.Выполнено Тогда
// Обработка завершена, отображаем результат
ОтключитьОбработчикОжидания("ПроверитьСтатусФоновогоЗадания");
ОчиститьЗапомненныйИдентификаторФоновогоЗадания();
Сообщить("Операция завершена.");
ИначеЕсли СостояниеЗадания = СостояниеФоновогоЗадания.Ошибка Тогда
// Обработка завершена с ошибкой
ОтключитьОбработчикОжидания("ПроверитьСтатусФоновогоЗадания");
ОчиститьЗапомненныйИдентификаторФоновогоЗадания();
Сообщить("Операция завершена с ошибкой.");
Иначе
// Обновляем индикатор прогресса
// ПроцентВыполнения = ...
// ЭлементыФормы.ПолеПрогресса.Значение = ПроцентВыполнения;
КонецЕсли;
КонецПроцедуры
В этом примере СообщитьПрогресс может быть собственной функцией или использовать механизмы БСП, если она присутствует в конфигурации. Для реализации подобного алгоритма без библиотечных подсистем подойдёт пример фонового выполнения операций с индикацией прогресса без БСП.
Один из ключевых вопросов, поднятых автором, заключался в возможности реализации без изменения конфигурации. Это особенно важно для внешних обработок, которые по своей сути предназначены для расширения функционала без вмешательства в типовые объекты. Проблема в том, что фоновые задания обычно требуют ссылки на экспортные процедуры общих модулей, которые зарегистрированы в конфигурации. Внешняя обработка же не является частью конфигурации.
Для решения этой задачи нам потребуется "прослойка" – общий модуль, который уже находится в конфигурации. Этот общий модуль будет выполнять роль посредника:
ВнешниеОбработки.Создать().Давайте подробнее рассмотрим каждый шаг:
Ваша внешняя обработка должна иметь экспортную процедуру, которая будет выполнять длительную операцию. Например:
// Модуль объекта внешней обработки
Процедура ВыполнитьДлительнуюОперацию(Параметры) Экспорт
// Здесь мы будем выполнять основную работу.
// Параметры могут содержать данные, переданные с клиента.
ОбщееКоличество = 100;
Для Итерация = 1 По ОбщееКоличество Цикл
// ... Ваш код длительной операции ...
// Сообщаем прогресс
Процент = Цел(Итерация / ОбщееКоличество * 100);
СообщениеПрогресса = "Обработано " + Итерация + " из " + ОбщееКоличество;
// Для демонстрации, как это можно сделать без БСП,
// сохраним прогресс в таблицу временного хранения или регистр сведений
// А затем на клиенте будем опрашивать эти данные по ИД фонового задания.
// Более правильный подход с Состояние() или БСП будет рассмотрен далее.
ЗаписьЛогаПрогресса(ИдентификаторФоновогоЗадания(), Процент, СообщениеПрогресса);
// Чтобы не перегружать сервер частыми обновлениями
Если Итерация % 5 = 0 Тогда
ВнешниеОбработки.УстановитьПрогресс(Процент, СообщениеПрогресса); // Если обработка запущена как доп.обработка через механизм платформы
КонецЕсли;
КонецЦикла;
КонецПроцедуры
// Вспомогательная процедура, если необходимо сохранять прогресс
// в какой-либо временный источник, опрашиваемый клиентом
Процедура ЗаписьЛогаПрогресса(ИдентификаторФоновогоЗадания, Процент, Сообщение)
// Здесь может быть код для записи в регистр сведений,
// таблицу временного хранения или другое место,
// откуда клиент может прочитать состояние по ИдентификаторуФоновогоЗадания.
// Например:
// МенеджерВременныхДанных.ОбновитьПрогресс(ИдентификаторФоновогоЗадания, Процент, Сообщение);
КонецПроцедуры
Вам потребуется создать (или использовать существующий) общий модуль в конфигурации, с установленными флажками "Сервер", "Вызов сервера" и, возможно, "Глобальный" (зависит от вашей архитектуры). В этом модуле мы разместим процедуру, которая будет запускаться фоновым заданием.
// Общий модуль (Сервер)
Процедура ЗапуститьДлительнуюОперациюВнешнейОбработки(АдресВХранилищеОбработки, ПараметрыДляОбработки, ИдентификаторФоновогоЗадания) Экспорт
Попытка
// Создаем объект внешней обработки из двоичных данных
ОбработкаОбъект = ВнешниеОбработки.Создать(АдресВХранилищеОбработки);
// Устанавливаем идентификатор текущего фонового задания,
// чтобы обработка могла его использовать для записи прогресса, если необходимо.
// Это может быть сделано через публичное свойство или метод обработки.
Если ТипЗнч(ОбработкаОбъект) = Тип("ВнешняяОбработкаОбъект") Тогда
Попытка
ОбработкаОбъект.ИдентификаторФоновогоЗадания = ИдентификаторФоновогоЗадания;
Исключение
// Свойство не найдено, это нормально, если обработка не предусматривает такой способ передачи
КонецПопытки;
КонецЕсли;
// Передаем параметры в обработку. Это может быть структура, массив и т.д.
ОбработкаОбъект.Параметры = ПараметрыДляОбработки;
// Вызываем экспортную процедуру внешней обработки
ОбработкаОбъект.ВыполнитьДлительнуюОперацию(ПараметрыДляОбработки);
// Завершение с сообщением
ФоновыеЗадания.ТекущееФоновоеЗадание().УстановитьСостояние("Выполнено успешно.");
Исключение Как Искл
// Логируем ошибку и устанавливаем состояние фонового задания как "Ошибка"
ЗаписьЖурналаРегистрации(
"ВнешниеОбработки.ДлительныеОперации",
УровеньВажности.Ошибка,
,,
"Ошибка при выполнении длительной операции внешней обработки: " + Искл.Описание
);
ФоновыеЗадания.ТекущееФоновоеЗадание().УстановитьСостояние("Ошибка: " + Искл.Описание);
// Перевыбрасываем исключение, чтобы оно было зафиксировано в журнале фоновых заданий
ВызватьИсключение Искл.Описание;
КонецПопытки;
КонецПроцедуры
// Процедура для чтения прогресса с клиента
Процедура ПолучитьПрогрессФоновогоЗадания(ИдентификаторФоновогоЗадания) Экспорт
// Здесь должен быть код, который читает сохраненный прогресс
// Например, из регистра сведений, где его сохраняла длительная операция.
// Если используется БСП, то это ДлительныеОперацииКлиентСервер.ПрочитатьПрогресс.
// В простейшем случае, можно использовать ФоновыеЗадания.Состояние()
// и ФоновыеЗадания.ПолучитьСообщения() для получения текстовых сообщений.
Состояние = ФоновыеЗадания.Состояние(ИдентификаторФоновогоЗадания);
Процент = 0;
Описание = "";
Если Состояние = СостояниеФоновогоЗадания.Выполняется Тогда
// Пример получения прогресса из сообщений фонового задания
// (если внутри операции вызывался ФоновыеЗадания.ТекущееФоновоеЗадание().СообщитьПрогресс(Процент, ОписаниеПрогресса);)
Для Каждого Сообщение Из ФоновыеЗадания.ПолучитьСообщения(ИдентификаторФоновогоЗадания) Цикл
Если Сообщение.ТипСообщения = ТипСообщенияФоновогоЗадания.Прогресс Тогда
Процент = Сообщение.Прогресс;
Описание = Сообщение.Описание;
КонецЕсли;
КонецЦикла;
ИначеЕсли Состояние = СостояниеФоновогоЗадания.Выполнено Тогда
Процент = 100;
Описание = "Завершено.";
ИначеЕсли Состояние = СостояниеФоновогоЗадания.Ошибка Тогда
Процент = 100; // Прогресс не актуален, но показываем, что дошли до конца
Описание = "Ошибка: " + ФоновыеЗадания.ПолучитьОшибку(ИдентификаторФоновогоЗадания);
КонецЕсли;
Возврат Новый Структура("Состояние, Процент, Описание", Состояние, Процент, Описание);
КонецПроцедуры
На клиентской стороне внешней обработки мы инициируем весь процесс:
ОбработчикОжидания для периодического опроса статуса фонового задания.
// Модуль формы внешней обработки (Клиент)
Перем мИдентификаторФоновогоЗадания Экспорт; // Храним ИД фонового задания
&НаКлиенте
Процедура ЗапуститьДлительнуюОперацию(Команда)
// Очищаем предыдущий прогресс
Элементы.ПрогрессПоле.Значение = 0;
Элементы.СостояниеТекст.Заголовок = "Ожидание...";
// 1. Считываем двоичные данные текущей внешней обработки
// ТекущаяВнешняяОбработка - это объект самой обработки.
// Но для запуска фонового задания нам нужен ее "файл", то есть двоичные данные.
// Если обработка запускается как ДополнительнаяОбработка, то можно получить ДвоичныеДанные из объекта ДополнительнаяОбработка.
// Если это просто файл .epf, то придется его сначала загрузить.
// Предположим, что мы можем получить ссылку на файл обработки.
// Пример: получаем двоичные данные внешней обработки, если она уже загружена
ИмяФайлаОбработки = ЭтотОбъект.ИмяФайла; // Если обработка сохранена как файл
Если НЕ ЗначениеЗаполнено(ИмяФайлаОбработки) Тогда
Сообщить("Не удалось определить имя файла обработки. Сохраните обработку перед запуском.", СтатусСообщения.Важное);
Возврат;
КонецЕсли;
// Можно прочитать файл обработки, если он лежит локально
Попытка
ДвоичныеДанныеОбработки = Новый ДвоичныеДанные(ИмяФайлаОбработки);
Исключение
Сообщить("Не удалось прочитать двоичные данные обработки по пути: " + ИмяФайлаОбработки, СтатусСообщения.Важное);
Возврат;
КонецПопытки;
// 2. Помещаем двоичные данные во временное хранилище на сервере
АдресВХранилищеОбработки = ПоместитьДанныеВоВременноеХранилищеНаСервере(ДвоичныеДанныеОбработки);
// 3. Формируем параметры для самой длительной операции внутри обработки
ПараметрыДляОбработки = Новый Структура("НазваниеПараметра", "ЗначениеПараметра");
// 4. Запускаем фоновое задание через общий модуль-прослойку
// Передаем адрес хранилища, параметры и уникальный ИД, который может использоваться для отслеживания
МенеджерФоновыхЗаданийНаСервере(АдресВХранилищеОбработки, ПараметрыДляОбработки);
// 5. Подключаем обработчик ожидания для проверки статуса
ПодключитьОбработчикОжидания("ПроверитьПрогрессНаСервере", 1, Истина); // Проверять каждую секунду
КонецПроцедуры
&НаКлиенте
Процедура ПроверитьПрогрессНаСервере()
Если мИдентификаторФоновогоЗадания = Неопределено Тогда
ОтключитьОбработчикОжидания("ПроверитьПрогрессНаСервере");
Возврат;
КонецЕсли;
Результат = ПолучитьПрогрессССервера(мИдентификаторФоновогоЗадания);
Элементы.ПрогрессПоле.Значение = Результат.Процент;
Элементы.СостояниеТекст.Заголовок = Результат.Описание;
Если Результат.Состояние = СостояниеФоновогоЗадания.Выполнено Тогда
Элементы.СостояниеТекст.Заголовок = "Операция успешно завершена!";
ОтключитьОбработчикОжидания("ПроверитьПрогрессНаСервере");
мИдентификаторФоновогоЗадания = Неопределено;
ИначеЕсли Результат.Состояние = СостояниеФоновогоЗадания.Ошибка Тогда
Элементы.СостояниеТекст.Заголовок = "Операция завершена с ошибкой: " + Результат.Описание;
ОтключитьОбработчикОжидания("ПроверитьПрогрессНаСервере");
мИдентификаторФоновогоЗадания = Неопределено;
КонецЕсли;
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПоместитьДанныеВоВременноеХранилищеНаСервере(Данные)
Возврат ПоместитьВоВременноеХранилище(Данные);
КонецФункции
&НаСервереБезКонтекста
Процедура МенеджерФоновыхЗаданийНаСервере(АдресВХранилищеОбработки, ПараметрыДляОбработки)
// Запускаем фоновое задание, передавая имя процедуры общего модуля
// и параметры для этой процедуры.
мИдентификаторФоновогоЗадания = ФоновыеЗадания.Выполнить(
"ОбщийМодульПрослойка.ЗапуститьДлительнуюОперациюВнешнейОбработки",
Новый Структура(
"АдресВХранилищеОбработки, ПараметрыДляОбработки, ИдентификаторФоновогоЗадания",
АдресВХранилищеОбработки, ПараметрыДляОбработки, UUID
),
,
"Выполнение длительной операции внешней обработки"
).УникальныйИдентификатор;
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПолучитьПрогрессССервера(ИдентификаторФоновогоЗадания)
Возврат ОбщийМодульПрослойка.ПолучитьПрогрессФоновогоЗадания(ИдентификаторФоновогоЗадания);
КонецФункции
Таким образом, мы используем минимальные изменения в конфигурации (только один общий модуль), чтобы предоставить полноценный механизм для запуска и отслеживания прогресса внешних обработок в фоновых заданиях.
Если ваша конфигурация использует Библиотеку Стандартных Подсистем (БСП), то задача значительно упрощается, так как БСП предоставляет готовый и хорошо отлаженный механизм для работы с длительными операциями и отображением прогресса. В этом случае, вам не нужно разрабатывать собственную прослойку и механизм отслеживания, достаточно использовать стандартные функции БСП.
Основные компоненты БСП для длительных операций:
ДлительныеОперации.ВыполнитьВФоне() или ДлительныеОперации.ВыполнитьПроцедуруМодуляОбъектаОбработки(): Эти функции позволяют запускать экспортные процедуры в фоновом режиме. Вторая специально предназначена для работы с внешними обработками.ДлительныеОперации.СообщитьПрогресс(): Вызывается внутри длительной операции на сервере для обновления информации о ходе выполнения.ДлительныеОперацииКлиент.ОжидатьЗавершение(): Клиентская функция, которая отображает форму ожидания и периодически опрашивает сервер о статусе и прогрессе выполнения фонового задания, автоматически обновляя индикатор.ДлительныеОперацииКлиентСервер.ПрочитатьПрогресс(): Серверная функция для чтения прогресса.Процесс использования БСП выглядит следующим образом:
ДлительныеОперации.СообщитьПрогресс(Процент, ТекстСообщения).ДлительныеОперации.ВыполнитьПроцедуруМодуляОбъектаОбработки(...) или ДлительныеОперации.ВыполнитьВФоне(...), передавая ссылку на вашу обработку и процедуру.ДлительныеОперацииКлиент.ОжидатьЗавершение(...), передавая идентификатор фонового задания. Эта функция сама покажет пользователю форму ожидания с индикатором прогресса и результатом.
// Модуль объекта внешней обработки
Процедура МояДлительнаяОперация(Параметры) Экспорт
ОбщееКоличество = 1000;
Для И = 1 По ОбщееКоличество Цикл
// ... Выполняем полезную работу ...
// Сообщаем прогресс
Процент = Цел(И / ОбщееКоличество * 100);
ТекстСообщения = "Выполняется: " + Процент + "% (" + И + " из " + ОбщееКоличество + ")";
ДлительныеОперации.СообщитьПрогресс(Процент, ТекстСообщения);
// Дополнительная задержка для демонстрации
Если И % 50 = 0 Тогда
НачатьТранзакцию();
ЗафиксироватьТранзакцию();
КонецЕсли;
КонецЦикла;
КонецПроцедуры
// Модуль формы внешней обработки (Клиент)
&НаКлиенте
Процедура ЗапуститьДлительнуюОперациюБСП(Команда)
Параметры = Новый Структура("НазваниеПараметра", "ЗначениеПараметра");
// Запускаем процедуру модуля объекта текущей обработки в фоне через БСП
ИдентификаторЗадания = ДлительныеОперацииКлиент.ВыполнитьПроцедуруМодуляОбъектаОбработки(
ЭтотОбъект, // Текущая внешняя обработка
"МояДлительнаяОперация", // Имя экспортной процедуры в модуле объекта
Параметры, // Параметры для процедуры
, // Описание
, // Адрес временного хранилища формы, если нужно вернуть результат на форму
Истина // Ждать завершения с показом окна прогресса
);
Если ИдентификаторЗадания <> Неопределено Тогда
// Ожидаем завершения и отображаем прогресс.
// БСП сама позаботится о форме ожидания и опросе статуса.
Результат = ДлительныеОперацииКлиент.ОжидатьЗавершение(ИдентификаторЗадания);
Если Результат.ЗавершеноУспешно Тогда
Сообщить("Операция завершена успешно.");
// Обработать Результат.Значение
Иначе
Сообщить("Операция завершена с ошибкой: " + Результат.ОписаниеОшибки, СтатусСообщения.Важное);
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Использование БСП является предпочтительным подходом, если она уже включена в вашу конфигурацию, так как это значительно сокращает объем разрабатываемого кода и обеспечивает стабильность решения. Для готовой реализации подобного механизма без изменения метаданных конфигурации можно использовать внешнюю обработку с индикацией прогресса фонового задания.
С версии платформы 8.3.26 появился более современный и эффективный способ взаимодействия между сервером и клиентом – механизм уведомлений. Этот механизм позволяет серверу инициативно отправлять сообщения клиенту, не дожидаясь клиентского запроса. Это устраняет необходимость в постоянном "опросе" сервера со стороны клиента (техника polling), что снижает нагрузку на сеть и сервер.
Принцип работы:
МенеджерУведомленийКлиента.ОтправитьУведомление или МенеджерУведомленийКлиента.ОтправитьУведомлениеМенеджеруКлиента, передавая текущий процент выполнения и статус.
// Модуль объекта внешней обработки (Сервер)
Процедура ДлительнаяОперацияСУведомлениями(ИдентификаторКлиента, Параметры) Экспорт
ОбщееКоличество = 1000;
Для И = 1 По ОбщееКоличество Цикл
// ... Выполняем полезную работу ...
Процент = Цел(И / ОбщееКоличество * 100);
Состояние = Новый Структура("Процент, Сообщение", Процент, "Обработка данных: " + Процент + "%");
// Отправляем уведомление клиенту
МенеджерУведомленийКлиента.ОтправитьУведомление(ИдентификаторКлиента, Состояние);
// Для демонстрации
Если И % 50 = 0 Тогда
НачатьТранзакцию();
ЗафиксироватьТранзакцию();
КонецЕсли;
КонецЦикла;
// Отправляем финальное уведомление
Состояние = Новый Структура("Процент, Сообщение, Завершено", 100, "Операция завершена успешно!", Истина);
МенеджерУведомленийКлиента.ОтправитьУведомление(ИдентификаторКлиента, Состояние);
КонецПроцедуры
// Модуль формы внешней обработки (Клиент)
Перем мИдентификаторКлиента Экспорт; // Идентификатор текущей клиентской сессии
&НаКлиенте
Процедура ЗапуститьДлительнуюОперациюСУведомлениями(Команда)
Элементы.ПрогрессПоле.Значение = 0;
Элементы.СостояниеТекст.Заголовок = "Ожидание...";
// Получаем идентификатор текущей клиентской сессии
мИдентификаторКлиента = ПолучитьИдентификаторТекущегоКлиентаНаСервере();
// Подключаем обработчик уведомлений
ПодключитьОбработчикУведомленияКлиента("ОбработатьУведомлениеОПрогрессе");
// Запускаем фоновое задание, передавая ИД клиента и параметры
Параметры = Новый Структура("НазваниеПараметра", "ЗначениеПараметра");
ЗапуститьФоновоеЗаданиеДляУведомленийНаСервере(мИдентификаторКлиента, Параметры);
КонецПроцедуры
&НаКлиенте
Процедура ОбработатьУведомлениеОПрогрессе(Уведомление)
// Уведомление содержит данные, отправленные с сервера
Если ТипЗнч(Уведомление) = Тип("Структура") Тогда
Элементы.ПрогрессПоле.Значение = Уведомление.Процент;
Элементы.СостояниеТекст.Заголовок = Уведомление.Сообщение;
Если Уведомление.Свойство("Завершено") И Уведомление.Завершено Тогда
ОтключитьОбработчикУведомленияКлиента("ОбработатьУведомлениеОПрогрессе");
Сообщить("Операция успешно завершена!");
КонецЕсли;
КонецЕсли;
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПолучитьИдентификаторТекущегоКлиентаНаСервере()
Возврат МенеджерУведомленийКлиента.ИдентификаторТекущегоКлиента();
КонецФункции
&НаСервереБезКонтекста
Процедура ЗапуститьФоновоеЗаданиеДляУведомленийНаСервере(ИдентификаторКлиента, Параметры)
// Здесь мы снова используем общий модуль-прослойку
// или прямую ссылку на экспортную процедуру внешней обработки,
// если она зарегистрирована как дополнительная.
// Для простоты примера, представим, что ДлительнаяОперацияСУведомлениями
// находится в общем модуле или доступна напрямую.
ФоновыеЗадания.Выполнить(
"МодульОбъектаВнешнейОбработки.ДлительнаяОперацияСУведомлениями",
Новый Структура("ИдентификаторКлиента, Параметры", ИдентификаторКлиента, Параметры),
,
"Длительная операция с уведомлениями"
);
КонецПроцедуры
Механизм уведомлений является наиболее современным и эффективным способом обратной связи от сервера к клиенту, особенно для длительных операций, так как он реактивен и не требует постоянного опроса.
Независимо от выбранного способа получения информации о прогрессе (опрос, БСП, уведомления), нам необходимо отобразить эти данные на форме внешней обработки. Для этого мы обычно используем следующие элементы:
Число или Процент: Его можно использовать как индикатор прогресса. Привяжите его к реквизиту формы, например, Прогресс (тип: Число).СостояниеТекст (тип: Строка).ПолеПрогресса: Этот элемент специально предназначен для отображения процентного выполнения в виде полосы прогресса. Его значение также привязывается к числовому реквизиту формы.Каждый раз, когда клиентский обработчик ожидания или обработчик уведомлений получает новую информацию о прогрессе, он должен обновлять соответствующие реквизиты формы, которые затем автоматически отображаются на элементах управления.
// В модуле формы, в обработчике обновления прогресса:
&НаКлиенте
Процедура ОбновитьИндикаторПрогресса(Процент, Сообщение)
Если Процент >= 0 И Процент <= 100 Тогда
ЭтаФорма.Элементы.ПолеПрогресса.Значение = Процент; // Если есть элемент "ПолеПрогресса"
ЭтаФорма.РеквизитПрогресса = Процент; // Обновляем реквизит, к которому привязан элемент
КонецЕсли;
ЭтаФорма.Элементы.ТекстовыйИндикатор.Заголовок = Сообщение; // Обновляем текстовый индикатор
ЭтаФорма.ОбновитьОтображениеДанных(); // При необходимости
КонецПроцедуры
Важно создать на форме внешней обработки соответствующие элементы и привязать их к реквизитам формы, чтобы динамически обновлять информацию для пользователя.
Реализация прогресса выполнения длительной серверной процедуры во внешней обработке 1С — это задача, требующая комплексного подхода. Мы рассмотрели несколько основных методов: использование фоновых заданий с прослойкой для внешней обработки, применение функционала БСП "Длительные операции" и современный механизм уведомлений платформы. Выбор конкретного метода будет зависеть от версии платформы, наличия БСП в конфигурации и требований к реализации без изменения основной конфигурации. В любом случае, предоставление пользователю обратной связи о ходе выполнения значительно улучшает пользовательский опыт и повышает доверие к системе.