Рассмотрим распространенную ситуацию, когда вы используете внешнюю обработку, которая запускает длительный фоновый процесс и отображает прогресс выполнения с помощью индикатора. Все отлично работает, пока вы открываете эту обработку напрямую. Однако, как только вы пытаетесь открыть ее форму программно из расширения, фоновый процесс продолжает свою работу, но индикатор выполнения (progressbar) на форме остается неподвижным. Давайте разберемся, почему так происходит и как мы можем это исправить.
Прежде чем погружаться в детали реализации, нам необходимо убедиться, что мы можем эффективно отлаживать код, выполняющийся в фоновых заданиях. Часто фоновые задания работают в отдельных сеансах, которые по умолчанию не подключаются к отладчику. Выясним, как настроить отладку.
Настройка автоматического подключения: Откройте конфигуратор, перейдите в меню "Отладка", затем выберите "Подключение". В открывшемся окне нажмите кнопку "Автоматическое подключение" и установите флажок напротив пункта "Фоновые задания". Это позволит отладчику автоматически подключаться к сеансам фоновых заданий, и ваши точки останова будут срабатывать.
Принудительное выполнение в текущем сеансе: Если точки останова все еще не срабатывают или вы хотите упростить отладку, можно временно изменить параметр конфигурации. В конфигураторе перейдите в меню "Сервис" -> "Параметры". На вкладке "Запуск 1С:Предприятия" найдите параметр "РежимОтладки" и установите его в значение "Разрешить" (или "Разрешить с остановкой"). Это заставит фоновые задания выполняться в текущем сеансе, что значительно упрощает отладку, так как они будут доступны в том же контексте, что и основной код.
Теперь, когда мы можем отлаживать фоновые задания, давайте проанализируем основные причины, по которым индикатор выполнения не обновляется в нашем сценарии.
Потеря контекста выполнения: Когда вы запускаете обработку автономно, контекст выполнения более прозрачен. Однако при вызове формы из расширения и запуске фонового задания, это задание может выполняться в отдельном сеансе на сервере. В результате, у фонового задания теряется "связь" с конкретной клиентской формой, которая его запустила, и оно не может напрямую обновить ее визуальные элементы.
Особенности клиент-серверного взаимодействия в управляемых формах: В управляемых формах, которые используются в расширениях, отсутствует постоянное и прямое соединение клиентской части с сервером. Взаимодействие происходит через сервер 1С, и код разделен на клиентский и серверный. Это означает, что серверный код фонового задания не имеет прямого доступа к элементам интерфейса на клиенте и не может их обновлять напрямую. Для обновления интерфейса необходимо инициировать действие с клиентской стороны или использовать специальные механизмы уведомлений.
Механизм "Длительные операции" БСП: Большинство типовых конфигураций 1С используют подсистему "Длительные операции" из Библиотеки стандартных подсистем (БСП) для отображения прогресса выполнения. Этот механизм включает в себя набор функций для запуска фоновых заданий, отслеживания их статуса и обновления индикатора на клиентской форме. Если ваша обработка или расширение не интегрированы с этим механизмом или не воспроизводят его логику, индикатор, скорее всего, не будет обновляться.
Принцип работы индикатора в управляемой форме: Для отображения прогресса в управляемых формах обычно создается реквизит формы числового типа (например, ПроцентВыполнения), который связывается с элементом формы типа "Поле индикатора". В процессе выполнения длительной операции значение этого реквизита необходимо периодически обновлять. Клиентская часть формы должна "знать", когда и как получить актуальное значение прогресса от фонового задания и обновить связанный реквизит.
Теперь, когда мы понимаем причины, рассмотрим два основных способа, как можно реализовать механизм обновления индикатора выполнения из фонового задания.
Этот подход широко использовался до появления более совершенных механизмов уведомлений. Он основан на том, что клиентская часть формы периодически опрашивает сервер на предмет статуса фонового задания и его прогресса. Разберем по шагам, как это реализовать.
Создание реквизита и элемента формы: На форме вашей обработки создайте реквизит, например, ПроцентВыполнения, с типом Число. Затем на форме разместите элемент "Поле индикатора" и свяжите его с этим реквизитом.
Запуск фонового задания: В серверном коде, который запускает длительную операцию, вызывайте метод для создания фонового задания. Важно, чтобы фоновое задание в процессе своей работы периодически сообщало о прогрессе. Если вы используете БСП, это может быть функция типа ДлительныеОперации.СообщитьПрогресс().
// Пример запуска фонового задания
ФоновоеЗадание = ФоновыеЗадания.Выполнить(
"МодульФоновогоЗадания.ДлительнаяОперация",
ПараметрыВыполнения,
"Длительная операция",
УникальныйИдентификаторЗадания
);
Внутри метода ДлительнаяОперация вашего общего модуля (или модуля фонового задания) периодически обновляйте прогресс:
// В МодульФоновогоЗадания.ДлительнаяОперация
Для Инд = 1 По 100 Цикл
// Выполнение части работы
Сообщить("Выполняется шаг " + Инд);
Процент = Инд; // Вычисляем текущий процент выполнения
// Если используем БСП, то так:
// ДлительныеОперации.СообщитьПрогресс(Процент, "Выполняется: " + Процент + "%");
// Если без БСП, то можно сохранять прогресс в хранилище значений,
// чтобы клиент мог его получить
// ХранилищеЗначений.Поместить(Процент, УникальныйИдентификаторЗадания);
ЗавершитьТекущееДвижение(); // Для возможности прерывания
КонецЦикла;
Установка обработчика ожидания на клиенте: После запуска фонового задания на клиентской форме установите обработчик ожидания, который будет периодически вызывать серверный метод для получения статуса фонового задания.
&НаКлиенте
Процедура ЗапуститьОперациюНаСервереИОбновитьИндикатор()
// ... Запускаем фоновое задание на сервере ...
// Сохраняем УИД фонового задания в реквизит формы
ЭтаФорма.ФоновоеЗаданиеУИД = ФоновоеЗадание.УникальныйИдентификатор;
// Устанавливаем обработчик ожидания для обновления индикатора
УстановитьОбработчикОжидания("ОбновитьИндикатор", 1); // Опрашивать каждую секунду
КонецПроцедуры
&НаКлиенте
Процедура ОбновитьИндикатор()
Если ЭтаФорма.ФоновоеЗаданиеУИД = Неопределено Тогда
Возврат;
КонецЕсли;
// Вызываем серверный метод для получения прогресса
Процент = ПолучитьПрогрессФоновогоЗаданияНаСервере(ЭтаФорма.ФоновоеЗаданиеУИД);
Если Процент = Неопределено Тогда // Задание еще не началось или завершилось
// Снимаем обработчик ожидания
ОтменитьОбработчикОжидания("ОбновитьИндикатор");
ЭтаФорма.ФоновоеЗаданиеУИД = Неопределено;
ЭтаФорма.ПроцентВыполнения = 0; // Сброс индикатора
Иначе
ЭтаФорма.ПроцентВыполнения = Процент;
КонецЕсли;
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПолучитьПрогрессФоновогоЗаданияНаСервере(УИДФоновогоЗадания)
Задание = ФоновыеЗадания.НайтиПоУникальномуИдентификатору(УИДФоновогоЗадания);
Если Задание = Неопределено Тогда
Возврат Неопределено;
КонецЕсли;
// Если используем БСП, то так:
// Возврат ДлительныеОперации.ПолучитьПрогресс(УИДФоновогоЗадания);
// Если без БСП, то получаем из хранилища
// Возврат ХранилищеЗначений.Получить(УИДФоновогоЗадания);
// Для примера, если фоновое задание возвращает результат в Параметры.Состояние
Если Задание.Состояние = СостояниеФоновогоЗадания.Выполняется Тогда
Возврат Задание.ПроцентВыполнения; // Если есть такой реквизит
ИначеЕсли Задание.Состояние = СостояниеФоновогоЗадания.Выполнено Тогда
Возврат 100;
Иначе
Возврат Неопределено;
КонецЕсли;
КонецФункции
Важный момент: Не забудьте снять обработчик ожидания, когда фоновое задание завершится, чтобы избежать лишних серверных вызовов.
Начиная с версии платформы 8.3.26, появился более эффективный механизм – отправка уведомлений с сервера на клиент. Этот подход значительно сокращает количество серверных вызовов, так как фоновое задание само "сообщает" клиенту о своем прогрессе, а не клиент постоянно опрашивает сервер. Разберем его по шагам.
Создание реквизита и элемента формы: Аналогично предыдущему методу, создайте реквизит ПроцентВыполнения (тип Число) и свяжите его с элементом "Поле индикатора" на форме.
Запуск фонового задания и отправка уведомлений: В коде фонового задания, кроме выполнения основной работы, периодически отправляйте уведомления клиенту с текущим прогрессом. Для этого используем объект УведомленияКлиента.
// В МодульФоновогоЗадания.ДлительнаяОперация
// ...
Для Инд = 1 По 100 Цикл
// Выполнение части работы
Процент = Инд;
// Отправляем уведомление на клиент
ПараметрыУведомления = Новый Соответствие();
ПараметрыУведомления.Вставить("Процент", Процент);
ПараметрыУведомления.Вставить("ИдентификаторЗадания", УникальныйИдентификаторЗадания); // Важно для фильтрации
УведомленияКлиента.ОтправитьУведомление(
"ОбновитьИндикаторПрогресса", // Имя события уведомления
ПараметрыУведомления
);
ЗавершитьТекущееДвижение();
КонецЦикла;
// По завершении можно отправить уведомление о 100% и завершении
ПараметрыУведомления = Новый Соответствие();
ПараметрыУведомления.Вставить("Процент", 100);
ПараметрыУведомления.Вставить("ИдентификаторЗадания", УникальныйИдентификаторЗадания);
ПараметрыУведомления.Вставить("Завершено", Истина);
УведомленияКлиента.ОтправитьУведомление("ОбновитьИндикаторПрогресса", ПараметрыУведомления);
Обратите внимание, что мы передаем ИдентификаторЗадания, чтобы клиентская форма могла определить, относится ли это уведомление к ее фоновому заданию.
Регистрация обработчика уведомлений на клиенте: На клиентской форме необходимо зарегистрировать обработчик для получения этих уведомлений. Это делается с помощью метода ПодключитьОбработчикОповещения.
&НаКлиенте
Перем ИдентификаторТекущегоФоновогоЗадания;
&НаКлиенте
Процедура ПриОткрытии(Отказ)
// ...
// Регистрация обработчика уведомлений
ПодключитьОбработчикОповещения("ОбновитьИндикаторПрогресса", ЭтотОбъект, "ОбработкаПолученияУведомленияОПрогрессе");
// ...
КонецПроцедуры
&НаКлиенте
Процедура ПриЗакрытии()
// Отключаем обработчик при закрытии формы
ОтключитьОбработчикОповещения("ОбновитьИндикаторПрогресса", ЭтотОбъект, "ОбработкаПолученияУведомленияОПрогрессе");
КонецПроцедуры
&НаКлиенте
Процедура ЗапуститьОперациюНаСервере()
// ... Запускаем фоновое задание на сервере ...
// Сохраняем УИД фонового задания, чтобы фильтровать уведомления
ИдентификаторТекущегоФоновогоЗадания = ФоновоеЗадание.УникальныйИдентификатор;
ЭтаФорма.ПроцентВыполнения = 0; // Сброс индикатора
КонецПроцедуры
&НаКлиенте
Процедура ОбработкаПолученияУведомленияОПрогрессе(ПараметрыУведомления) Экспорт
Если ПараметрыУведомления.ИдентификаторЗадания = ИдентификаторТекущегоФоновогоЗадания Тогда
ЭтаФорма.ПроцентВыполнения = ПараметрыУведомления.Процент;
Если ПараметрыУведомления.Свойство("Завершено") И ПараметрыУведомления.Завершено Тогда
// Задание завершено, сбрасываем идентификатор
ИдентификаторТекущегоФоновогоЗадания = Неопределено;
Сообщить("Операция завершена!");
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Таким образом, клиентская форма будет получать уведомления только от "своего" фонового задания и обновлять индикатор по мере их поступления.
Особенности работы с расширениями: Важно помнить, что расширения в 1С работают преимущественно с управляемыми формами. Открытие формы из расширения подразумевает совмещение изменений между основной конфигурацией и расширением. Хотя это не является прямой причиной неотображения индикатора, это может влиять на общую производительность и отзывчивость интерфейса. Однако, описанные выше методы обновления индикатора будут работать и в контексте расширений, поскольку они учитывают архитектуру клиент-серверного взаимодействия управляемых форм.
Выбирайте наиболее подходящий для вашей версии платформы и архитектуры решения метод. Механизм уведомлений клиента предпочтительнее для современных конфигураций благодаря своей эффективности и отзывчивости. Успешной разработки!
← К списку