Всем добрый день!
В этой заметке хочу кратко рассказать свой опыт написания функции, выполняемой в фоне. Решение пригодно для клиент-серверного варианта работы 1С, с использованием БСП версии 3.1.11.392. Проверяла в конфигурации 1С: УТ 11.5.22.149.
Предыстория: у меня появилась задача выполнять обращение к api методу одного из наших популярных маркетплейсов. Из документации к нужному мне api методу узнала, что за один запрос сервер может вернуть не более 100 000 строк записей. А в 1 минуту можно отправлять не более одного такого запроса. При попытке зациклить обращения по api, на очередной итерации получила ответ сервера с кодом 429, что соответствует ошибке "слишком много запросов в единицу времени". Поэтому стало нужно использовать новый для меня метод 1С ВызватьПаузу(), доступный на платформе 8.3.25 и выше. При этом оказалось что ВызватьПаузу() возможно использовать только в фоновых заданиях. И тут я потратила достаточно времени для поиска простого примера, как же правильно выполнить мою функцию в фоне.
Как оказалось, сложное вначале оказывается на поверку достаточно простым. Хочу с вами поделиться готовым примером. Пример состоит из двух частей и предназначен для клиент-серверного варианта работы 1С и с использованием БСП:
- Часть 1. Расширение с общим модулем, для описания в нем самой фоновой функции. В архиве для скачивания это файл ТестовоеРасширениеФЗ.cfe
- Часть 2. Внешняя обработка, которая на форму выводит результат работы фоновой функции. В архиве для скачивания это файл ТестированиеФЗ.epf
Расписываю по шагам действия и привожу тексты процедур.
- Создать в расширении общий модуль, в свойствах модуля указать "Выполнение на сервере в клиент-серверном варианте".
- В этом общем модуле делаем процедуру с признаком "Экспорт". Сама процедура тестовая, смысловой нагрузки не несет. Главное, что она будет выполнена в фоне.
- Во внешней обработке, из ее управляемой формы делаем вызов клиентской функции "ВыполнитьФоновуюПроцедуру()", эта процедура делает вызов фоновой функции из моего общего модуля и помещает результат работы на форму.
И на третьем шаге небольшая 1С магия. Или, как мне встретилось на одном сайте, посвященном 1С, автор это назвал "заклинания 1С", что насмешило и запомнилось. Процедура (или функция), выполняемая в фоне, не может вернуть результат своей работы напрямую. А может только отдать адрес временного хранилища, куда помещает результат своей работы. И чтобы все-таки получить результат, сначала нужно объявить адрес временного хранилища и потом этот адрес передавать в фоновую процедуру или функцию.
Итак, создаем внешнюю обработку, в ней форму обработки, на форме делаем кнопку с вызовом клиентской процедуры "ВыполнитьФоновуюПроцедуру(Команда)", текст процедуры:
&НаКлиенте
Процедура ВыполнитьФоновуюПроцедуру(Команда)
ПараметрыЗапуска = Новый Структура;
ПараметрыЗапуска.Вставить("ДатаНач", ДатаОтбора.ДатаНачала );
ПараметрыЗапуска.Вставить("ДатаКон", ДатаОтбора.ДатаОкончания );
СтруктураФоновогоЗадания = ВыполнитьФоновоеЗаданиеНаСервере(ПараметрыЗапуска, УникальныйИдентификатор);
ПараметрыОжидания = ДлительныеОперацииКлиент.ПараметрыОжидания(ЭтотОбъект);
ОповещениеОЗавершении = Новый ОписаниеОповещения("ОбработатьДанные", ЭтотОбъект);
ДлительныеОперацииКлиент.ОжидатьЗавершение(СтруктураФоновогоЗадания, ОповещениеОЗавершении, ПараметрыОжидания);
КонецПроцедуры
Процедура вызывает серверную процедуру "ВыполнитьФоновоеЗаданиеНаСервере(ПараметрыЗапуска, УникальныйИдентификатор)", процедура также расположена в модуле формы нашей внешней обработки. Текст:
&НаСервере
Функция ВыполнитьФоновоеЗаданиеНаСервере(ПараметрыЗапуска, УИ)
ТекущееХранилище = ПоместитьВоВременноеХранилище(Неопределено, УИ); //объявление адреса временнного хранилища
ПараметрыВыполнения = ДлительныеОперации.ПараметрыВыполненияВФоне(УИ); //процедура 1с УТ 11
ПараметрыВыполнения.АдресРезультата = ТекущееХранилище;
ПараметрыВыполнения.НаименованиеФоновогоЗадания = НСтр("ru = 'Получить тестовые данные'");
ПараметрыВыполнения.ЗапуститьВФоне = Истина;
ПараметрыВыполнения.ОжидатьЗавершение = 0;
СтруктураФоновогоЗадания = ДлительныеОперации.ВыполнитьПроцедуру(ПараметрыВыполнения,
"алФоновыеЗадания.алВыполнитьВВФоне",
ПараметрыЗапуска,
ПараметрыВыполнения );
Возврат СтруктураФоновогоЗадания;
КонецФункции
Также в процедуре "ВыполнитьФоновуюПроцедуру" объявлено оповещение "ОбработатьДанные", которое сработает при окончании выполнения фоновой функции.
Текст процедуры оповещения "ОбработатьДанные":
&НаКлиенте
Процедура ОбработатьДанные(Результат, ДополнительныеПараметры) Экспорт
Если Результат = Неопределено Тогда
Возврат;
ИначеЕсли Результат.Статус = "Выполнено" Тогда
СтруктураРезультат= ПолучитьИзВременногоХранилища(Результат.АдресРезультата);
Если СтруктураРезультат<> Неопределено Тогда
алЗаписатьДанныеВФорму(СтруктураРезультат);
КонецЕсли;
КонецЕсли;
КонецПроцедуры
В оповещении "ОбработатьДанные" происходит получение из временного хранилища данных от фоновой функции, и вывод результата на форму в алЗаписатьДанныеВФорму(СтруктураРез)
&НаСервере
Процедура алЗаписатьДанныеВФорму(СтруктураРезультат);
Переменная_А = СтруктураРезультат.Парам1;
Переменная_Б = СтруктураРезультат.Парам2;
КонецПроцедуры
И, конечно, сама процедура из общего модуля "алФоновыеЗадания", выполняемая в фоне, и использующая метод ВызватьПаузу(), который мне был нужен. Переменные "Дата1, Дата2" просто обозначила из передачу из обработки, смысла в них в этом примере нет.
&НаСервере
функция алВыполнитьВФоне(ПараметрыПроцедуры, ПараметрыФоновогоЗадания) экспорт
//Дата1, Дата2 не использую, просто показываю что параметры переданы и доступны.
алДатаНачала = ПараметрыПроцедуры.ДатаНач;
алДатаОкончания = ПараметрыПроцедуры.ДатаКон;
//на примере с датами удобно увидеть работу функции "ВызватьПаузу()"
Парам_А = ТекущаяДата(); //получаем текущую дату
ВызватьПаузу(3000); //ждем 3 секунды, работает только в фоновом режиме
Парам_Б = ТекущаяДата(); //получаем заново тек.дату, должна быть на 3 секунды старше Парам_А
СтруктураРезультат = новый Структура("Парам1, Парам2", Парам_А , Парам_Б );
ПоместитьВоВременноеХранилище(новый Структура("СтруктураРезультат", СтруктураРезультат),
ПараметрыФоновогоЗадания.АдресРезультата);
КонецФункции
Вот, собственно, и вся магия. Текст функции алВыполнитьВФоне() заменяете любой своей логикой, главное в её конце поместить результат работы во временное хранилище. А в оповещении получить этот результат из него же.
Внешняя обработка и расширение доступны для скачивания.
Всё проверяла на 1С:Предприятие 8.3 (8.3.27.1786), конфигурация Управление торговлей, редакция 11 (11.5.22.149).
Надеюсь, эта заметка кому-то принесет пользу.
Проверено на следующих конфигурациях и релизах:
- Управление торговлей, редакция 11, релизы 11.5.22.149
Вступайте в нашу телеграмм-группу Инфостарт