Как передать данные с сервера 1С на клиент в фоновом режиме без зависания интерфейса и мерцания курсора?

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

При разработке интерактивных интерфейсов в системе 1С:Предприятие часто возникает задача оперативного оповещения пользователя о событиях, происходящих на сервере. Это может быть готовность тяжелого отчета, появление нового заказа в базе данных, изменение статуса задачи или новое сообщение в чате. Классический подход с использованием метода ПодключитьОбработчикОжидания имеет существенный недостаток: при каждом обращении клиента к серверу через контекстные или бесконтекстные вызовы курсор мыши пользователя на доли секунды превращается в «колёсико» (песочные часы). Если опрос происходит часто (например, раз в секунду), интерфейс начинает раздражающе моргать, усложняя комфортную работу.

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

Почему стандартный обработчик ожидания вызывает мерцание курсора?

При вызове серверной процедуры из кода управляемой формы (даже с директивой &НаСервереБезКонтекста) платформа 1С временно переводит поток клиентского приложения в режим ожидания ответа. Платформа сигнализирует операционной системе о выполнении блокирующей сетевой операции. Операционная система Windows или Linux реагирует на это изменением стандартного указателя мыши на системный курсор занятости. Даже если серверная процедура абсолютно пустая и выполняется за 1-2 миллисекунды, визуальный стек ОС успевает отрисовать анимацию загрузки. Результатом становится постоянное микро-моргание экрана.

Для решения этой проблемы нам необходимо исключить прямые синхронные вызовы сервера 1С из контекста выполнения клиентской формы. Рассмотрим, как это сделать программными методами.

---

Решение 1. Опрос сервера через HTTP-сервисы 1С

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

Платформа выполняет запросы через объект HTTPСоединение на низком сетевом уровне, не переводя поток интерфейса формы в состояние блокирующего серверного вызова. Благодаря этому операционная система не меняет курсор мыши, и интерфейс для пользователя остается абсолютно статичным и комфортным.

Рассмотрим по шагам, как реализовать этот механизм:

Шаг 1. Создание и публикация HTTP-сервиса

В дереве метаданных конфигурации добавим новый объект HTTP-сервисы с именем ОбменКлиентСервер. Укажем корневой URL как exchange. Добавим шаблон URL с именем ПолучитьДанные и шаблоном /get_data/{ИмяПользователя}. Для этого шаблона определим метод GET и укажем имя обработчика в модуле HTTP-сервиса.

В модуле HTTP-сервиса напишем код, который будет проверять наличие подготовленных данных для конкретного пользователя во временном хранилище или специальном регистре сведений:


Функция ПолучитьДанныеGET(Запрос)
    
    ИмяПользователя = Запрос.ПараметрыURL.Получить("ИмяПользователя");
    
    // Получаем данные, подготовленные для этого клиента на сервере
    ДанныеДляОтправки = ПроверитьНовыеСобытияНаСервере(ИмяПользователя);
    
    Ответ = Новый HTTPСервисОтвет(200);
    Ответ.Заголовки.Вставить("Content-Type", "application/json; charset=utf-8");
    
    ЗаписьJSON = Новый ЗаписьJSON;
    ЗаписьJSON.УстановитьСтроку();
    ЗаписатьJSON(ЗаписьJSON, ДанныеДляОтправки);
    СтрокаJSON = ЗаписьJSON.Закрыть();
    
    Ответ.УстановитьТелоИзСтроки(СтрокаJSON, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
    
    Возврат Ответ;
    
КонецФункции

После написания кода необходимо опубликовать HTTP-сервис на веб-сервере (IIS или Apache) через стандартный диалог публикации в Конфигураторе.

Шаг 2. Настройка опроса на стороне клиента

В модуле управляемой формы при открытии подключаем стандартный обработчик ожидания с небольшим интервалом (например, 1 или 2 секунды):


&НаКлиенте
Процедура ПриОткрытии(Отказ)
    ПодключитьОбработчикОжидания("ПроверитьОбновленияЧерезHTTP", 2, Ложь);
КонецПроцедуры

В процедуре обработчика ожидания мы создаем асинхронное подключение к нашему опубликованному веб-серверу и отправляем GET-запрос:


&НаКлиенте
Процедура ПроверитьОбновленияЧерезHTTP()
    
    // Параметры подключения к нашему веб-серверу
    СерверАдрес = "localhost";
    Порт = 80;
    БазаИмя = "Base1C";
    
    HTTPСоединение = Новый HTTPСоединение(СерверАдрес, Порт, "ПользовательРобот", "ПарольРобота");
    HTTPЗапрос = Новый HTTPЗапрос("/" + БазаИмя + "/hs/exchange/get_data/" + ИмяПользователяПриложения);
    
    Попытка
        HTTPОтвет = HTTPСоединение.Получить(HTTPЗапрос);
        Если HTTPОтвет.КодСостояния = 200 Тогда
            СтрокаJSON = HTTPОтвет.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8);
            ОбработатьПолученныеДанные(СтрокаJSON);
        КонецЕсли;
    Исключение
        // Подавляем сетевые ошибки, чтобы не прерывать сеанс пользователя
        ЗаписьЖурналаРегистрации("Ошибка фонового опроса HTTP", УровеньЖурналаРегистрации.Предупреждение,,, ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
    КонецПопытки;
    
КонецПроцедуры

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

---

Решение 2. Использование поля HTML-документа и технологии WebSockets / SSE

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

Мы можем разместить на форме скрытое или маленькое поле HTML-документа, внутри которого будет крутиться легковесный JavaScript-сценарий. Этот сценарий может взаимодействовать с внешним веб-сервером по протоколу WebSockets или технологии SSE (Server-Sent Events).

Разберем детально схему реализации этого метода:

  1. На управляемую форму добавляется реквизит с типом Строка и видом отображения Поле HTML-документа.
  2. При создании формы на сервере мы генерируем простой HTML-код со встроенным JS-скриптом, который открывает постоянное WebSocket-соединение с брокером сообщений или промежуточным веб-сервером.
  3. Когда на сервере 1С генерируется событие, сервер отправляет пакет данных в WebSocket-канал.
  4. JS-скрипт внутри поля HTML-документа мгновенно ловит это событие и передает данные обратно в среду 1С.

Посмотрим на пример генерации HTML-содержимого для инициализации сокета:


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    
    // Формируем HTML с JS-скриптом подключения к WebSocket-серверу
    ТекстHTML = 
    "<html>
    |<head>
    |<script>
    |   var socket;
    |   function initConnect() {
    |       socket = new WebSocket('ws://localhost:8080/events');
    |       socket.onmessage = function(event) {
    |           // Передаем данные обратно в платформу 1С
    |           window.location.href = 'v8event:' + encodeURIComponent(event.data);
    |       };
    |       socket.onclose = function() {
    |           setTimeout(initConnect, 5000); // Автореконнект при обрыве связи
    |       };
    |   }
    |   window.onload = initConnect;
    |</script>
    |</head>
    |<body style='background-color:transparent;margin:0;padding:0;'></body>
    |</html>";
    
    СодержимоеHTMLПоля = ТекстHTML;
    
КонецПроцедуры

Для приема сообщений из JavaScript на стороне 1С мы используем стандартное событие поля HTML-документа ПриНажатии (в старых версиях платформы через парсинг изменения навигации) или событие УправлениеКартой/ПриНажатии. В современных версиях платформы (начиная с 8.3.14) доступна более простая схема взаимодействия через объект window.v8exception или явный вызов процедур 1С из кода JavaScript:


&НаКлиенте
Процедура СодержимоеHTMLПоляПриНажатии(Элемент, ДанныеСобытия, СтандартнаяОбработка)
    
    СсылкаПерехода = ДанныеСобытия.Document.location.href;
    Если Найти(СсылкаПерехода, "v8event:") > 0 Тогда
        СтандартнаяОбработка = Ложь;
        ДанныеСтрокой = Сред(СсылкаПерехода, Найти(СсылкаПерехода, "v8event:") + 8);
        ДекодированныеДанные = ДекодироватьСтроку(ДанныеСтрокой, СпособКодированияСтроки.КодировкаURL);
        
        // Обрабатываем событие, пришедшее из сокета в реальном времени!
        ВыполнитьДействиеПоСобытию(ДекодированныеДанные);
    КонецЕсли;
    
КонецПроцедуры

Этот подход гарантирует 100% отсутствие блокировок и мерцания интерфейса, так как сетевое соединение удерживается силами изолированного браузерного движка.

---

Решение 3. Система взаимодействия и облачный сервис «1С:Диалоги»

Начиная с версии платформы 8.3.11, разработчики 1С представили штатный механизм асинхронного двустороннего взаимодействия — Систему взаимодействия. Это полноценный сервер сообщений, глубоко интегрированный в платформу.

Многие разработчики ошибочно полагают, что для использования Системы взаимодействия обязательно разворачивать локальный сервер (1С:Сервер взаимодействия), требующий отдельного лицензирования и администрирования СУБД PostgreSQL/Elasticsearch. На самом деле для большинства прикладных задач можно бесплатно использовать облачный сервис 1С:Диалоги, предоставляемый самой фирмой «1С».

При использовании Системы взаимодействия доставка сообщений с сервера на конкретный клиент происходит мгновенно по постоянному TCP-соединению, которое платформа удерживает в фоновом режиме. Никакого мерцания курсора при этом не происходит.

Проанализируем пример отправки сообщения с сервера конкретному пользователю:


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

Для получения этих сообщений на клиенте используется обработчик интеграции или подписка на события сообщений в модуле управляемого приложения:


&НаКлиенте
Процедура ПриНачалеРаботыСистемы()
    ПодключитьОбработчикНовыхСообщений();
КонецПроцедуры

&НаКлиенте
Процедура ПодключитьОбработчикНовыхСообщений()
    // Регистрируем обработчик для входящих сообщений Системы взаимодействия
    СистемаВзаимодействия.ПодключитьОбработчикНовыхСообщений(Новый ОписаниеОповещения("ОбработатьНовоеСообщение", ЭтотОбъект));
КонецПроцедуры

&НаКлиенте
Процедура ОбработатьНовоеСообщение(Сообщение, ДополнительныеПараметры) Экспорт
    // Отображаем уведомление пользователю без перезагрузки формы
    ПоказатьОповещениеПользователя("Новое событие на сервере!",, Сообщение.Текст);
КонецПроцедуры

Использование встроенной Системы взаимодействия — наиболее перспективный и поддерживаемый вендором способ организации асинхронного обмена данными.

---

Решение 4. Разработка внешней компоненты (NativeAPI / C#) для старых версий платформы

Если ваша конфигурация работает в режиме совместимости с ранними версиями платформы (например, 8.3.6, как у автора темы на форуме), встроенная Система взаимодействия вам недоступна. В этом случае идеальным технологическим решением будет использование технологии NativeAPI и создание внешней компоненты на C++ или C#.

Суть метода заключается в делегировании сетевой работы операционной системе за рамками процесса 1С:

Посмотрим на пример инициализации внешней компоненты в модуле 1С:


&НаКлиенте
Процедура ПриОткрытииФормы()
    
    // Подключаем внешнюю компоненту, написанную на C# или C++
    Попытка
        УстановитьВнешнююКомпоненту("ОбщиеМакеты.КомпонентаАсинхронногоОбмена");
        ПодключитьВнешнююКомпоненту("ОбщиеМакеты.КомпонентаАсинхронногоОбмена", "AsyncSocket", ТипВнешнейКомпоненты.Native);
        
        Компонента = Новый("AddIn.AsyncSocket.SocketClient");
        // Передаем параметры подключения и запускаем прослушивание порта в фоне
        Компонента.ОткрытьСоединение("192.168.1.100", 9000, ИмяПользователяПриложения);
    Исключение
        ЗаписьЖурналаРегистрации("Внешняя компонента", УровеньЖурналаРегистрации.Ошибка,,, ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
    КонецПопытки;
    
КонецПроцедуры

После этого на форме достаточно определить обработчик внешнего события:


&НаКлиенте
Процедура ОбработкаВнешнегоСобытия(Источник, Событие, Данные)
    
    Если Источник = "AsyncSocket" И Событие = "NewMessage" Тогда
        // Данные пришли с сервера мгновенно, без опроса! Курсор не моргал!
        ОбработатьПолученныеДанные(Данные);
    КонецЕсли;
    
КонецПроцедуры

Это самый производительный способ для старых платформ, полностью снимающий нагрузку с сетевой подсистемы 1С на клиенте.

---

Решение 5. Современные асинхронные возможности платформы (8.3.21+ и 8.3.27+)

Если у вас есть возможность обновить версию платформы 1С до актуального релиза, то проблема решается штатными методами на современном технологическом стеке:

Асинхронные HTTP-запросы (с платформы 8.3.21)
В платформу были добавлены неблокирующие методы для работы с HTTP. Теперь при вызове методов ВызватьHTTPМетодАсинх() или ПолучитьАсинх() с использованием конструкции Ждать, платформа не блокирует интерфейсный поток операционной системы. Фоновый опрос выполняется полностью незаметно для пользователя.

Штатный WebSocket-клиент (с платформы 8.3.27)
В платформу добавлен долгожданный объект WebSocketКлиент. С его помощью 1С может держать постоянное двустороннее соединение с любым внешним сервером сокетов (например, RabbitMQ, сервером телефонии или NodeJS-сервером) напрямую из кода 1С без использования полей HTML-документа или сторонних DLL. Доставка событий происходит мгновенно, не нагружает сеть циклическими опросами и гарантирует идеальное поведение курсора мыши.

---

Сравнительный анализ решений

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

  1. Опрос через HTTP-сервисы:
    • Сложность реализации: Низкая
    • Совместимость с версиями платформы: Любые версии платформы v8.3
    • Требования к инфраструктуре: Требуется веб-сервер (IIS/Apache)
    • Мерцание курсора: Отсутствует
  2. Поле HTML-документа + JS:
    • Сложность реализации: Средняя
    • Совместимость с версиями платформы: От 8.3.6 и выше
    • Требования к инфраструктуре: Нужен внешний сервер сокетов
    • Мерцание курсора: Отсутствует
  3. Система взаимодействия:
    • Сложность реализации: Низкая
    • Совместимость с версиями платформы: От 8.3.11 и выше
    • Требования к инфраструктуре: Облако «1С:Диалоги» или локальный сервер
    • Мерцание курсора: Отсутствует
  4. Внешняя компонента NativeAPI:
    • Сложность реализации: Высокая (требует навыков C#/C++)
    • Совместимость с версиями платформы: Любые версии платформы v8
    • Требования к инфраструктуре: Сервер сокетов
    • Мерцание курсора: Отсутствует
  5. WebSocket-клиент 1С (8.3.27+):
    • Сложность реализации: Низкая
    • Совместимость с версиями платформы: Релизы 8.3.27 и новее
    • Требования к инфраструктуре: Сервер сокетов
    • Мерцание курсора: Отсутствует

Выбирайте наиболее подходящий способ в зависимости от требований вашего проекта и ограничений используемой версии платформы 1С:Предприятие. Успешной разработки!

← На главную