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