В современной практике интеграции 1С с внешними сервисами (будь то маркетплейсы, государственные информационные системы вроде «Меркурия» и «Честного Знака», логистические компании или нейросети) разработчики все чаще сталкиваются с асинхронным API. Классическая схема взаимодействия выглядит так:
Task ID).Главная сложность заключается в организации процесса ожидания статуса. Если запустить обычный цикл опроса на клиенте, интерфейс программы намертво «зависнет», и пользователь не сможет продолжить работу. Если выполнять опрос в цикле на сервере без задержек, процессор сервера будет нагружен на 100%, а внешняя система мгновенно заблокирует ваши запросы, вернув ошибку HTTP 429 Too Many Requests (Превышен лимит запросов). Метод ПодключитьОбработчикОжидания(), который часто используют для задержек, работает исключительно на клиенте и недоступен в серверных процедурах.
В этой статье мы подробно разберем, как правильно и технологично решить эту задачу на платформе 1С:Предприятие, используя современные паттерны проектирования, механизмы фоновых заданий, новые возможности платформы и возможности БСП.
---
Это классический и самый надежный архитектурный подход, который работает на любых версиях платформы 1С:Предприятие 8. Он полностью переносит логику ожидания на сервер, не блокируя работу пользователя.
Суть метода: Вместо того чтобы ждать ответ «здесь и сейчас», мы фиксируем необходимость проверки во вспомогательном регистре сведений, а обработку этой очереди доверяем независимому фоновому или регламентному заданию.
Разберем реализацию этого паттерна по шагам:
Шаг 1. Создание регистра сведений
Создадим непериодический независимый регистр сведений ОчередьЗадачAPI со следующей структурой:
ИдентификаторЗадачи (Строка, длина 100) — уникальный ID, полученный от внешнего API.Статус (ПеречислениеСсылка.СтатусыЗадачAPI или Строка) — текущее состояние задачи («Создана», «ВРаботе», «Выполнена», «Ошибка»).ДатаПоследнейПроверки (Дата) — время последней отправки запроса.КоличествоПопыток (Число) — счетчик проверок, чтобы избежать бесконечного опроса «зависших» задач.ПараметрыЗапроса (ХранилищеЗначения) — сериализованные данные, которые могут понадобиться для постобработки результата (например, ссылка на документ в 1С, который инициировал запрос).Шаг 2. Регистрация задачи при отправке
При отправке первичного запроса мы записываем полученный идентификатор в наш регистр:
Процедура ЗарегистрироватьЗадачуВAPI(ДокументСсылка, ДанныеЗапроса)
// 1. Отправляем первичный запрос к API
ИдентификаторЗадачи = ОтправитьПервичныйЗапросВСервис(ДанныеЗапроса);
Если ЗначениеЗаполнено(ИдентификаторЗадачи) Тогда
// 2. Записываем задачу в очередь для последующего опроса
МенеджерЗаписи = РегистрыСведений.ОчередьЗадачAPI.СоздатьМенеджерЗаписи();
МенеджерЗаписи.ИдентификаторЗадачи = ИдентификаторЗадачи;
МенеджерЗаписи.Статус = "Создана";
МенеджерЗаписи.ДатаПоследнейПроверки = ТекущаяДатаСеанса();
МенеджерЗаписи.КоличествоПопыток = 0;
МенеджерЗаписи.ПараметрыЗапроса = Новый ХранилищеЗначения(ДокументСсылка);
МенеджерЗаписи.Записать(Истина);
// 3. Опционально: динамически запускаем фоновое задание проверки
ФоновыеЗадания.Создать("ИнтеграцияAPI.ПроверитьСтатусыЗадач");
КонецЕсли;
КонецПроцедуры
Шаг 3. Обработка очереди на сервере
На сервере создается процедура общего модуля (например, в модуле ИнтеграцияAPI с флагами «Сервер», «Вызов сервера»), которая считывает все незавершенные задачи из регистра, опрашивает API и обрабатывает результаты. Для исключения перегрузки внешнего сервиса мы проверяем только те задачи, с момента последней проверки которых прошло, например, более 10 секунд.
Процедура ПроверитьСтатусыЗадач() Экспорт
// Получаем незавершенные задачи, по которым еще не превышен лимит попыток
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Очередь.ИдентификаторЗадачи КАК ИдентификаторЗадачи,
| Очередь.КоличествоПопыток КАК КоличествоПопыток,
| Очередь.ПараметрыЗапроса КАК ПараметрыЗапроса
|ИЗ
| РегистрСведений.ОчередьЗадачAPI КАК Очередь
|ГДЕ
| Очередь.Статус В (""Создана"", ""ВРаботе"")
| И РАЗНОСТЬДАТ(Очередь.ДатаПоследнейПроверки, &ТекущееВремя, СЕКУНДА) >= 10
| И Очередь.КоличествоПопыток < 20";
Запрос.УстановитьПараметр("ТекущееВремя", ТекущаяДатаСеанса());
РезультатЗапроса = Запрос.Выполнить();
Если РезультатЗапроса.Пустой() Тогда
Возврат;
КонецЕсли;
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
ИдентификаторЗадачи = Выборка.ИдентификаторЗадачи;
НовыйСтатус = ПолучитьСтатусЗадачиИзAPI(ИдентификаторЗадачи);
МенеджерЗаписи = РегистрыСведений.ОчередьЗадачAPI.СоздатьМенеджерЗаписи();
МенеджерЗаписи.ИдентификаторЗадачи = ИдентификаторЗадачи;
МенеджерЗаписи.Прочитать();
Если МенеджерЗаписи.Выбран() Тогда
МенеджерЗаписи.ДатаПоследнейПроверки = ТекущаяДатаСеанса();
МенеджерЗаписи.КоличествоПопыток = МенеджерЗаписи.КоличествоПопыток + 1;
Если НовыйСтатус = "Выполнена" Тогда
// Получаем результат и обрабатываем его
РезультатВыполнения = ПолучитьРезультатЗадачиИзAPI(ИдентификаторЗадачи);
ДокументСсылка = МенеджерЗаписи.ПараметрыЗапроса.Получить();
ОбработатьРезультатИнтеграции(ДокументСсылка, РезультатВыполнения);
// Удаляем задачу из очереди или закрываем статус
МенеджерЗаписи.Статус = "Выполнена";
МенеджерЗаписи.Записать(Истина);
ИначеЕсли НовыйСтатус = "Ошибка" Тогда
МенеджерЗаписи.Статус = "Ошибка";
МенеджерЗаписи.Записать(Истина);
ЗаписатьВЖурналРегистрации("Интеграция API", УровеньЖурналаРегистрации.Ошибка, , ,
"Ошибка выполнения задачи на стороне сервиса: " + ИдентификаторЗадачи);
Иначе
// Если статус "ВРаботе" - просто обновляем параметры в регистре
МенеджерЗаписи.Статус = "ВРаботе";
МенеджерЗаписи.Записать(Истина);
КонецЕсли;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Для регулярного запуска этой проверки мы можем настроить стандартное Регламентное задание в конфигураторе, указав расписание (например, «каждые 15 секунд»). Это гарантирует, что даже если пользователь закроет программу, данные все равно будут успешно получены и обработаны сервером.
---
Если логика бизнес-процесса требует запустить проверку статуса в рамках одного сеанса (например, в контексте фонового задания, запущенного под конкретную интерактивную операцию пользователя), нам необходим инструмент для безопасной приостановки выполнения серверного кода.
Раньше разработчики использовали сомнительные «костыли» (вызов внешней команды ping с таймаутом через ЗапуститьПриложение, бесконечные циклы с проверкой времени, сильно нагружающие процессор, или внешние компоненты). Начиная с версии платформы 8.3.25, в платформе появился штатный, элегантный механизм.
Новая возможность: Метод глобального контекста ВызватьПаузу(<Интервал>).
Этот метод временно приостанавливает поток выполнения текущего сеанса на указанный интервал (в миллисекундах). Важно отметить, что во время паузы процессорные ресурсы сервера практически не расходуются.
Рассмотрим пример использования метода в фоновом задании, запущенном из клиентской формы:
// Функция выполняется на сервере в фоновом задании
Функция ОпроситьAPIССервернойПаузой(ИдентификаторЗадачи) Экспорт
МаксимальноеКоличествоПопыток = 10;
ИнтервалОжиданияСекунд = 5;
ТекущаяПопытка = 0;
Пока ТекущаяПопытка < МаксимальноеКоличествоПопыток Цикл
Статус = ПолучитьСтатусЗадачиИзAPI(ИдентификаторЗадачи);
Если Статус = "Выполнена" Тогда
Возврат ПолучитьРезультатЗадачиИзAPI(ИдентификаторЗадачи);
ИначеЕсли Статус = "Ошибка" Тогда
ВызватьИсключение "Ошибка выполнения задачи на стороне API!";
КонецЕсли;
ТекущаяПопытка = ТекущаяПопытка + 1;
// Предотвращаем ошибку 429 Too Many Requests, делая паузу.
// Переводим секунды в миллисекунды: 5 * 1000 = 5000 мс.
ВызватьПаузу(ИнтервалОжиданияСекунд * 1000);
КонецЦикла;
ВызватьИсключение "Превышено время ожидания готовности задачи.";
КонецФункции
Важные ограничения метода `ВызватьПаузу`:
---
Если вам необходимо организовать неблокирующий опрос с клиента, платформа предлагает использовать современную асинхронную модель на базе ключевых слов Асинх и Ждать.
Проанализируем ситуацию: мы запускаем фоновое задание для опроса API на сервере, а на клиенте асинхронно ждем его завершения, сохраняя полную отзывчивость интерфейса (пользователь может открывать другие вкладки, проводить документы и общаться в чате).
&НаКлиенте
Асинх Процедура ЗапуститьПолучениеДанныхАсинхронно(ИдентификаторЗадачи)
// Запускаем на сервере фоновое задание, которое будет делать проверку API
// и записывать результат во временное хранилище (или регистр).
ПараметрыЗадания = Новый Массив;
ПараметрыЗадания.Добавить(ИдентификаторЗадачи);
АдресХранилища = ПоместитьВоВременноеХранилище(Неопределено, УникальныйИдентификатор);
ПараметрыЗадания.Добавить(АдресХранилища);
Задание = ЗапуститьФоновоеЗаданиеНаСервере("ИнтеграцияAPI.ОпроситьAPIИЗаписатьРезультат", ПараметрыЗадания);
// Асинхронно ждем завершения фонового задания без блокировки UI.
// Метод "ОжидатьЗавершенияВыполненияАсинх" возвращает обещание (Promise).
Попытка
РезультатОжидания = Ждать ФоновыеЗадания.ОжидатьЗавершенияВыполненияАсинх(Задание, 60); // таймаут 60 сек
// Получаем результат из временного хранилища
ДанныеРезультата = ПолучитьРезультатИзХранилища(АдресХранилища);
ПоказатьПредупреждение(, "Данные успешно получены и обработаны!");
ОбновитьДанныеФормы(ДанныеРезультата);
Исключение
ПоказатьПредупреждение(, "Произошла ошибка при получении данных: " + ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
Такой подход обеспечивает максимальный комфорт для конечного пользователя, полностью исключая эффект «зависания» программы.
---
Если вы разрабатываете решение на базе типовой конфигурации (например, 1С:ERP, УТ, КА, УНФ, БП), вам не нужно писать код для реализации пауз вручную. В Библиотеке Стандартных Подсистем (БСП) уже встроены надежные кроссплатформенные обертки.
Рассмотрим метод ОбщегоНазначения.Пауза(<Секунды>):
Пример вызова из общего модуля БСП:
// Приостанавливаем выполнение потока на сервере на 5 секунд
ОбщегоНазначения.Пауза(5);
---
Если вы имеете возможность влиять на настройки интеграции со стороны внешнего сервиса или разрабатываете собственное API, постоянный опрос (Polling) — это архитектурный тупик. Наиболее современный подход — использование вебхуков (Webhooks).
Как это работает:
/v1/api_callback) и публикуется на веб-сервере (IIS или Apache).Callback_URL, указывающий на ваш опубликованный HTTP-сервис 1С.Этот метод полностью устраняет проблему ошибок 429, не требует настройки фоновых процессов ожидания и гарантирует моментальное получение результата без лишней нагрузки на сетевой трафик.
---
Для выбора оптимальной стратегии работы с асинхронным API воспользуйтесь следующей памяткой:
ВызватьПаузу или ОбщегоНазначения.Пауза) и асинхронного метода ожидания на клиенте Ждать ФоновыеЗадания.ОжидатьЗавершенияВыполненияАсинх.