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