Как устранить зацикливание управляемой формы при получении данных из веб-сервиса в событии ПриАктивизацииСтроки?

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

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

Почему происходит зацикливание формы?

Проанализируем механику взаимодействия платформы 1С и клиентского приложения. Событие ПриАктивизацииСтроки генерируется на клиенте каждый раз, когда меняется активная строка таблицы. Это происходит не только при физическом клике пользователя мышкой или нажатии стрелок на клавиатуре, но и при любом программном обновлении элементов формы, связанных с этой таблицей.

Когда мы выполняем контекстный серверный вызов (процедура с директивой &НаСервере) непосредственно внутри этого обработчика и в процессе работы модифицируем реквизиты формы (например, загружаем новые строки в подчиненную табличную часть с помощью метода Загрузить()), происходит следующее:

  1. Пользователь активизирует строку в основной таблице.
  2. Срабатывает клиентское событие ПриАктивизацииСтроки.
  3. Вызывается контекстный серверный метод, который обращается к веб-сервису, получает данные и перезаписывает подчиненную табличную часть формы.
  4. Платформа возвращает управление на клиент и производит обновление измененных данных формы.
  5. Обновление интерфейса и перерисовка связанных элементов заставляет платформу считать, что состояние таблиц изменилось, из-за чего на клиенте повторно генерируется событие ПриАктивизацииСтроки для основной таблицы.
  6. Происходит переход к шагу 3, образуя бесконечную рекурсию. Программа зависает и аварийно завершает работу по превышению стека вызовов.

Главное правило разработки управляемых форм: в обработчике события ПриАктивизацииСтроки категорически запрещено использовать контекстные серверные вызовы, изменяющие данные формы.

---

Решение проблемы: Безконтекстные вызовы и паттерн «Антидребезг»

Для создания отказоустойчивого интерфейса мы применим архитектурное решение, состоящее из трех ключевых правил:

  1. Использование бесконтекстных вызовов: Любые запросы к серверу или веб-сервису должны выполняться в функциях с директивой &НаСервереБезКонтекста. Они не передают на сервер тяжелый контекст всей формы и не инициируют автоматическое обновление её элементов при возврате.
  2. Реализация механизма «Антидребезг» (Debounce): Запрос к веб-сервису — это ресурсоемкая операция. Если пользователь быстро прокручивает список заказов, нет смысла отправлять запросы на каждую строку. Мы будем запускать загрузку данных с небольшой задержкой (например, 0.4 секунды) с помощью обработчика ожидания. Если пользователь перейдет на другую строку быстрее, чем истечет этот интервал, предыдущий запрос отменяется, а таймер запускается заново.
  3. Кэширование идентификатора строки: Мы добавим проверку, которая предотвратит повторную загрузку данных, если пользователь вернулся на ту строку, данные по которой уже отображаются на экране.

---

Пошаговый разбор практической реализации

Рассмотрим детально, как написать правильный и безопасный код в модуле управляемой формы.

Шаг 1. Объявление переменных в модуле формы

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


&НаКлиенте
Перем ТекущийИдентификаторЗаказа; // Хранит ИД строки, которую выделил пользователь
&НаКлиенте
Перем ЗагруженныйИдентификаторЗаказа; // Хранит ИД строки, данные которой уже отображены на форме

Шаг 2. Обработка активизации строки на клиенте

В обработчике события мы сбрасываем ранее подключенный таймер, обновляем значение переменной с идентификатором строки и заново взводим таймер ожидания на 0.4 секунды. Это гарантирует, что запрос к веб-сервису уйдет только тогда, когда пользователь остановит навигацию по списку.


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

Шаг 3. Обработчик ожидания и обновление формы

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


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

Шаг 4. Бесконтекстный запрос к веб-сервису

Для выполнения интеграционного запроса мы создаем функцию с директивой &НаСервереБезКонтекста. Она выполняет вызов веб-сервиса, десериализацию данных и возвращает готовую таблицу значений на клиент. Обратите внимание, что мы не обращаемся к элементам формы напрямую внутри этой процедуры.


&НаСервереБезКонтекста
Функция ПолучитьДанныеИзВебСервисаНаСервере(Знач УникальныйИдентификаторЗаказа)
    
    Попытка
        // Инициализируем WS-прокси
        Соединение = WSСсылки.SelectNom.СоздатьWSПрокси("SelectNom", "SelectNom", "SelectNomSoap");
        
        // Выполняем вызов веб-сервиса
        ОтветВебСервиса = Соединение.SelectNom(УникальныйИдентификаторЗаказа);
        
        // Десериализуем XML/JSON ответ во временную Таблицу Значений
        ТаблицаРезультат = ДесериализоватьОтвет(ОтветВебСервиса);
        
        Возврат ТаблицаРезультат;
        
    Исключение
        // Логируем ошибку, чтобы не прерывать работу пользователя аварийно
        ЗаписьЖурналаРегистрации("Интеграция.ВебСервис",
            УровеньЖурналаРегистрации.Ошибка,,,
            ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
        Возврат Неопределено;
    КонецПопытки;
    
КонецФункции

---

Продвинутый уровень: Использование фоновых заданий БСП

Описанный выше вариант с обработчиком ожидания отлично защищает форму от зацикливания и снижает нагрузку на базу данных при быстром скроллинге. Однако, если веб-сервис внешней системы работает нестабильно или долго отвечает (более 1–2 секунд), синхронный бесконтекстный вызов все равно заставит интерфейс пользователя «замереть» на время выполнения сетевого запроса.

Для создания по-настоящему профессионального решения рекомендуется использовать механизм асинхронных длительных операций на базе БСП (Библиотеки Стандартных Подсистем):

  1. В процедуре ВыполнитьЗагрузкуСоставаЗаказа вместо прямого вызова веб-сервиса мы инициируем фоновое задание с помощью метода ДлительныеОперации.ЗапуститьВыполнение().
  2. Пользователь сохраняет полный контроль над интерфейсом формы: он может продолжать листать список или переключать вкладки, программа не зависает.
  3. На клиенте запускается циклический обработчик ожидания, проверяющий статус выполнения фонового задания (метод ДлительныеОперации.ОжидатьУправление()).
  4. Как только фоновое задание возвращает статус «Выполнено», полученные данные считываются из временного хранилища и загружаются в подчиненную табличную часть. Если пользователь за это время успел переключиться на другую строку — результат старого фонового задания просто аннулируется.

Применение связки «Антидребезг + Безконтекстный вызов + Фоновое задание» является золотым стандартом разработки на платформе 1С:Предприятие 8.3 для любых задач интеграции в динамических формах.

← На главную