Как получить результат выполнения фонового задания без записи в базу данных

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В процессе разработки на платформе 1С часто возникает задача распараллеливания тяжелых вычислений. Однако стандартный подход с записью результата в справочник или регистр сведений не всегда оправдан: это создает лишнюю нагрузку на СУБД, раздувает базу и замедляет работу системы, особенно если данных много (например, огромные JSON-ответы от внешних сервисов). В этой статье мы подробно разберем, как передать результат из фонового задания обратно в вызывающий сеанс, используя только оперативную память и механизмы платформы.

Метод 1: Использование Временного хранилища (стандартный подход)

Основным механизмом для обмена данными между фоновым заданием и родительским сеансом является Временное хранилище. Однако здесь есть критически важный нюанс: платформа жестко контролирует время жизни данных в хранилище. Рассмотрим, как правильно организовать этот процесс, чтобы данные не исчезли в момент завершения задания.

Разберем алгоритм по шагам:

  1. В родительском сеансе (в форме) необходимо заранее подготовить «адрес» для будущего результата. Важно передать в функцию ПоместитьВоВременноеХранилище уникальный идентификатор формы. Это свяжет данные с временем жизни этой формы.
  2. Передать полученный адрес в параметры фонового задания.
  3. Внутри фонового задания поместить результат по этому адресу.

Проанализируем пример кода для запуска процесса из формы:


// Код в модуле управляемой формы
&НаКлиенте
Процедура ЗапуститьОбработку()
    
    // 1. Создаем адрес, привязанный к текущей форме
    АдресРезультата = ПоместитьВоВременноеХранилище(Неопределено, ЭтаФорма.УникальныйИдентификатор);
    
    ПараметрыПроцедуры = Новый Массив;
    ПараметрыПроцедуры.Добавить(ПараметрыЗапроса);
    ПараметрыПроцедуры.Добавить(АдресРезультата); // Передаем адрес как параметр
    
    // 2. Запускаем фоновое задание
    Задание = ФоновыеЗадания.Выполнить("МойОбщийМодуль.ВыполнитьРасчет", ПараметрыПроцедуры, КлючЗадания, "Расчет данных");
    ИдентификаторЗадания = Задание.УникальныйИдентификатор;
    
    // Подключаем обработчик ожидания для проверки статуса
    ПодключитьОбработчикОжидания("ПроверитьСтатусЗадания", 2);
    
КонецПроцедуры

Теперь рассмотрим, что должно происходить внутри экспортной процедуры в фоновом задании:


// Код в общем модуле (Сервер)
Процедура ВыполнитьРасчет(ПараметрыЗапроса, АдресРезультата) Экспорт
    
    РезультатСтруктура = Новый Структура;
    // ... выполняем тяжелые вычисления или HTTP-запрос ...
    РезультатСтруктура.Вставить("Данные", ПолученныеДанные);
    РезультатСтруктура.Вставить("Статус", "Успешно");
    
    // 3. Помещаем результат точно по переданному адресу
    ПоместитьВоВременноеХранилище(РезультатСтруктура, АдресРезультата);
    
КонецПроцедуры

Важный момент: если вы не передадите ИдентификаторФормы при инициализации адреса в родительском сеансе, платформа удалит данные из хранилища сразу после завершения потока фонового задания, и метод ПолучитьИзВременногоХранилища вернет Неопределено.

Метод 2: Передача данных через Сообщения пользователю

Этот метод часто называют «хаком», но он официально поддерживается платформой и крайне эффективен, когда вызов происходит не из формы (например, из другого фонового задания или Web-сервиса), где нет УникальногоИдентификатора формы. Механизм СообщениеПользователю позволяет передавать строки неограниченной длины, которые кэшируются в памяти фонового задания.

Рассмотрим реализацию этого метода. В фоновом задании мы используем объект СообщениеПользователю для упаковки данных (например, сериализованного JSON):


// Внутри фонового задания
Процедура ВыполнитьИПередатьЧерезСообщения() Экспорт
    ТекстРезультата = СформироватьОченьБольшойJSON(); 
    
    Сообщение = Новый СообщениеПользователю;
    Сообщение.Текст = ТекстРезультата;
    Сообщение.Сообщить();
КонецПроцедуры

В родительском сеансе мы опрашиваем состояние задания и извлекаем сообщения. Посмотрим на пример кода:


// В родительском сеансе
Задание = ФоновыеЗадания.НайтиПоУникальномуИдентификатору(ИдентификаторЗадания);
Если Задание.Состояние = СостояниеФоновогоЗадания.Завершено Тогда
    
    // Получаем массив сообщений, накопленных заданием
    МассивСообщений = Задание.ПолучитьСообщенияПользователю(Истина); // Истина очищает буфер
    
    Для Каждого Сообщение Из МассивСообщений Цикл
        ПолныйJSON = ПолныйJSON + Сообщение.Текст;
    КонецЦикла;
    
    // Обрабатываем результат
    Результат = ПрочитатьJSON(ПолныйJSON);
КонецЕсли;

Этот способ позволяет обмениваться данными «на лету». Вы можете передавать прогресс выполнения или части данных еще до того, как задание полностью завершится. Главное помнить, что данные передаются как строки, поэтому для сложных структур потребуется сериализация в JSON или XML.

Метод 3: Ожидание завершения и типичные ошибки

Выясним причину часто возникающей ошибки, когда ПолучитьИзВременногоХранилища возвращает пустое значение, хотя адрес передан верно. Проблема часто кроется в гонке условий (race condition).

Если вы используете самописный обработчик ожидания, может возникнуть ситуация, когда код проверки статуса срабатывает в тот момент, когда задание уже перешло в состояние Завершено, но менеджер временного хранилища еще не успел синхронизировать данные между сеансами.

Рассмотрим правильный подход к ожиданию через БСП (Библиотеку Стандартных Подсистем), который минимизирует такие риски:


// Использование типового механизма БСП
ПараметрыОжидания = ДлительныеОперацииКлиент.ПараметрыОжидания(ЭтотОбъект);
ПараметрыОжидания.ВыводитьПрогрессВыполнения = Истина;

ДлительныеОперацииКлиент.ОжидатьЗавершение(СтруктураЗадания, 
    Новый ОписаниеОповещения("ОбработатьРезультат", ЭтотОбъект), 
    ПараметрыОжидания);

Практический совет: если вы работаете в обычных формах, помните, что механизм временного хранилища там работает менее стабильно. В некоторых релизах платформы 8.3 результат может пропадать при повторных запусках одного и того же задания. В таких случаях рекомендуется использовать явное создание файлов во временном каталоге (через ПолучитьИмяВременногоФайла), если запись в БД категорически запрещена.

Сравнение методов передачи данных

Проанализируем, какой метод лучше выбрать в зависимости от условий задачи:

Выбирая между этими вариантами, всегда учитывайте контекст вызова. Если вы разрабатываете современное приложение на управляемых формах, придерживайтесь Метода 1 с использованием ДлительныеОперации. Если же ваша задача — высоконагруженный API-сервис, смотрите в сторону Метода 2, чтобы избежать проблем с привязкой к сессиям форм.

← На главную