При работе с большими объемами данных в 1С, например, при обработке сотен тысяч заказов, интерфейс программы может «зависать», вызывая дискомфорт у пользователя. Стандартным решением в таких случаях является использование фоновых заданий. Однако начинающие разработчики часто сталкиваются с проблемой: невозможно передать форму или объект обработки напрямую в фоновое задание, так как эти объекты не сериализуются. В этой статье мы подробно разберем, как правильно организовать заполнение табличной части из фонового процесса, обеспечив отзывчивость интерфейса и отображение прогресса выполнения.
Прежде всего, проанализируем технические ограничения платформы 1С:Предприятие 8.3. Фоновое задание запускается в отдельном сеансе на стороне сервера. У этого сеанса нет доступа к оперативной памяти клиентского приложения, где «живет» форма. Платформа использует механизм сериализации (преобразования объекта в поток данных) для передачи параметров между клиентом и сервером, но такие объекты, как УправляемаяФорма или ДанныеФормыСтруктура, не могут быть сериализованы. Наша задача — разделить логику на подготовку данных и их отображение.
Рассмотрим классический подход, который будет работать в любой конфигурации. Суть метода заключается в том, что фоновое задание формирует «безликую» ТаблицаЗначений, сохраняет ее в специальное хранилище на сервере, а форма по завершении задания забирает эти данные и заполняет свою табличную часть.
Разберем процесс по шагам:
Шаг 1. Подготовка процедуры запуска на сервере
Нам необходимо создать процедуру в модуле объекта обработки (или в общем модуле), которая будет выполнять расчеты. Важно помнить, что если обработка внешняя, фоновое задание должно иметь доступ к её коду. Рассмотрим пример запуска фонового задания из модуля формы:
&НаСервере
Функция ЗапуститьЗаполнениеНаСервере()
// Создаем уникальный адрес во временном хранилище для получения результата
АдресРезультата = ПоместитьВоВременноеХранилище(Неопределено, ЭтаФорма.УникальныйИдентификатор);
// Подготавливаем параметры для фонового задания
ПараметрыЗадания = Новый Структура;
ПараметрыЗадания.Вставить("ДатаНачала", Объект.ПериодНачала);
ПараметрыЗадания.Вставить("ДатаОкончания", Объект.ПериодОкончания);
ПараметрыЗадания.Вставить("АдресРезультата", АдресРезультата);
// Если обработка внешняя, нужно передать её двоичные данные или путь
НаименованиеЗадания = "Заполнение заказов в фоне";
// Запускаем задание. В данном примере вызывается экспортная процедура из общего модуля
Задание = ФоновыеЗадания.Выполнить("МойОбщийМодульСервер.СформироватьСписокЗаказов", ПараметрыЗадания, , НаименованиеЗадания);
Возврат Новый Структура("ИдентификаторЗадания, АдресРезультата", Задание.УникальныйИдентификатор, АдресРезультата);
КонецФункции
Шаг 2. Код фонового задания (Сервер)
В этой процедуре мы выполняем тяжелые запросы. Выясним причину медленной работы: часто это проверка связанных документов в цикле. Мы будем использовать Запрос с левым соединением, чтобы сразу отобрать заказы без реализаций.
Процедура СформироватьСписокЗаказов(Параметры, АдресРезультата) Экспорт
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ЗаказПокупателя.Ссылка КАК Заказ
|ИЗ
| Документ.ЗаказПокупателя КАК ЗаказПокупателя
| ЛЕВОЕ СОЕДИНЕНИЕ Документ.РеализацияТоваровУслуг КАК Реализация
| ПО Реализация.ДокументОснование = ЗаказПокупателя.Ссылка
|ГДЕ
| ЗаказПокупателя.Дата МЕЖДУ &ДатаНачала И &ДатаОкончания
| И Реализация.Ссылка ЕСТЬ NULL";
Запрос.УстановитьПараметр("ДатаНачала", Параметры.ДатаНачала);
Запрос.УстановитьПараметр("ДатаОкончания", Параметры.ДатаОкончания);
РезультатТЗ = Запрос.Выполнить().Выгрузить();
// Помещаем результат в хранилище, чтобы клиент мог его забрать
ПоместитьВоВременноеХранилище(РезультатТЗ, Параметры.АдресРезультата);
КонецПроцедуры
Чтобы форма «узнала» о завершении задания, не блокируя пользователя, используем ПодключитьОбработчикОжидания. Проанализируем состояние задания через определенные интервалы времени.
&НаКлиенте
Переменная ИдентификаторФоновогоЗадания;
Переменная АдресХранилищаРезультата;
&НаКлиенте
Процедура КомандаЗаполнить(Команда)
РезультатЗапуска = ЗапуститьЗаполнениеНаСервере();
ИдентификаторФоновогоЗадания = РезультатЗапуска.ИдентификаторЗадания;
АдресХранилищаРезультата = РезультатЗапуска.АдресРезультата;
// Блокируем только нужные элементы, а не всю форму
Элементы.ГруппаПараметры.Доступность = Ложь;
// Запускаем проверку каждые 2 секунды
ПодключитьОбработчикОжидания("ПроверитьЗавершениеЗадания", 2);
КонецПроцедуры
&НаКлиенте
Процедура ПроверитьЗавершениеЗадания()
Если ЗаданиеЗавершено(ИдентификаторФоновогоЗадания) Тогда
ОтключитьОбработчикОжидания("ПроверитьЗавершениеЗадания");
ЗагрузитьРезультатИзХранилища();
Элементы.ГруппаПараметры.Доступность = Истина;
КонецЕсли;
КонецПроцедуры
Если вы работаете в современной конфигурации (например, БП 3.0, ERP, УТ 11), настоятельно рекомендуем использовать стандартную подсистему «Длительные операции». Это позволит избежать написания «велосипедов» и обеспечит штатное отображение прогресс-бара.
Рассмотрим, как это упрощает код:
ДлительныеОперации.ЗапуститьВыполнениеНаСервере(). Она сама создаст фоновое задание и вернет структуру с идентификатором.ДлительныеОперации.СообщитьПрогресс(Процент, "Обработка данных...") (или внедрите индикатор прогресса в ячейке списка).ДлительныеОперации.ОперацияЗавершена() для проверки статуса.Пример получения данных через БСП:
&НаСервере
Процедура ЗавершениеФоновогоЗаданияНаСервере()
// Получаем данные из временного хранилища БСП
РезультатЗадания = ПолучитьИзВременногоХранилища(АдресХранилищаРезультата);
Если ТипЗнч(РезультатЗадания) = Тип("ТаблицаЗначений") Тогда
Объект.ТЧ_Заказы.Загрузить(РезультатЗадания);
КонецЕсли;
КонецПроцедуры
Выясним еще один важный момент: если в таблице значений после фонового задания оказывается 50 000 строк, прямая загрузка в ДанныеФормыКоллекция (ТЧ формы) на сервере может быть медленной. В этом случае проанализируем ситуацию и предложим альтернативу:
Ссылка. Все дополнительные поля (дата заказа, контрагент, сумма) динамический список или прикладной код подтянет позже.Заполнение табличной части из фонового задания — это не только перенос кода с клиента на сервер, но и правильная организация обмена данными. Мы рассмотрели метод с использованием ВременногоХранилища, который является наиболее гибким. Помните, что во время работы фонового задания пользователь может закрыть форму. Поэтому всегда указывайте УникальныйИдентификатор формы при помещении данных в хранилище, чтобы система автоматически очистила память после закрытия окна.