При работе с внешними компонентами или интерактивными данными, например, получаемыми через WebSocket, мы часто сталкиваемся с проблемой блокировки пользовательского интерфейса. Когда код выполнения занимает значительное время или должен постоянно "слушать" внешние события, это приводит к тому, что форма 1С перестает отвечать на действия пользователя. В этой статье мы подробно рассмотрим, как организовать фоновое выполнение такого кода, чтобы интерфейс оставался отзывчивым, и данные могли интерактивно отображаться на форме.
Изначальная проблема, с которой столкнулся наш коллега, заключалась в использовании цикла вида Пока Клиент.Принять(0, Данные) Цикл ... КонецЦикла; непосредственно при открытии формы. Такой подход, хоть и функционален для получения данных, полностью блокирует основной поток выполнения и делает интерфейс непригодным для работы. Цель же — постоянно получать данные с вебсокета и немедленно отображать их на форме.
Давайте проанализируем различные подходы и выберем наиболее подходящий для нашей задачи, учитывая возможности платформы 1С:Предприятие.
ПодключитьОбработчикОжидания)Один из самых простых и эффективных способов реализовать периодическую проверку событий на клиентской стороне без блокировки интерфейса – это механизм обработчика ожидания. Он позволяет выполнять определенную процедуру с заданным интервалом, когда система 1С находится в состоянии "покоя", то есть не занята другими интенсивными операциями.
ПодключитьОбработчикОжидания?Мы подключаем процедуру, указывая ее имя и интервал в секундах. Платформа будет вызывать эту процедуру каждый раз, когда истечет указанный интервал и система будет свободна. Важно понимать, что это не точный таймер – фактический интервал срабатывания может отличаться, особенно если в системе выполняются другие операции.
ПодключитьОбработчикОжидания(<ИмяПроцедуры>, <Интервал>, <Однократно>).Клиент.Принять().ОтключитьОбработчикОжидания(<ИмяПроцедуры>).Давайте посмотрим, как это будет выглядеть в коде модуля управляемой формы.
&НаКлиенте
Перем мКлиентВебсокета; // Предполагается, что это объект внешней компоненты
// Процедура инициализации и подключения обработчика
Процедура ПриОткрытии()
// Инициализация объекта Клиент. Предположим, что он создается где-то здесь.
// Например: мКлиентВебсокета = Новый ВнешняяКомпонентаВебсокет.Клиент();
// мКлиентВебсокета.Подключиться("ws://localhost:8080"); // Пример подключения
// Подключаем обработчик ожидания, который будет регулярно проверять вебсокет
// Интервал 0.1 секунды (100 миллисекунд) достаточно мал для интерактивности
// и достаточно велик, чтобы не перегружать процессор постоянными вызовами.
ПодключитьОбработчикОжидания("ПроверитьВебсокет", 0.1, Ложь);
Сообщить("Обработчик ожидания для вебсокета подключен.");
КонецПроцедуры
// Процедура, выполняемая обработчиком ожидания
&НаКлиенте
Процедура ПроверитьВебсокет()
Перем Данные;
// В цикле принимаем все доступные данные, чтобы не ждать следующего вызова обработчика
// Если Клиент.Принять() возвращает Истина, значит есть новые данные
Пока мКлиентВебсокета.Принять(0, Данные) Цикл
// Здесь мы обрабатываем полученные данные
// и обновляем форму.
Сообщить("Получены данные: " + Данные);
// Пример обновления элемента формы (например, ПолеHTMLДокумента или ПолеТекстовогоДокумента)
// Элементы.МоеПолеДляСообщений.Текст = Элементы.МоеПолеДляСообщений.Текст + Данные + Символы.ПС;
// Или добавление строки в таблицу на форме
// НоваяСтрока = Элементы.ТаблицаДанных.Добавить();
// НоваяСтрока.Сообщение = Данные;
// Важно: избегайте ресурсоемких операций внутри этого цикла,
// чтобы не вызвать блокировку, которую мы пытаемся избежать.
КонецЦикла;
КонецПроцедуры
// Процедура отключения обработчика при закрытии формы
Процедура ПриЗакрытии()
// Отключаем обработчик ожидания, чтобы он не продолжал работать после закрытия формы
ОтключитьОбработчикОжидания("ПроверитьВебсокет");
Сообщить("Обработчик ожидания для вебсокета отключен.");
// Также здесь можно корректно закрыть соединение вебсокета
// мКлиентВебсокета.Закрыть();
КонецПроцедуры
В этом примере мКлиентВебсокета – это переменная, в которой хранится объект вашей внешней компоненты. Мы подключаем обработчик в процедуре ПриОткрытии и отключаем его в ПриЗакрытии. Процедура ПроверитьВебсокет будет периодически вызываться, проверять наличие новых сообщений и обновлять форму.
Важно: этот подход имитирует "бесконечный цикл" путем частых проверок. Сам метод Клиент.Принять(0, Данные) с таймаутом 0 (или другим маленьким значением) позволяет проверить наличие данных без длительного ожидания, что идеально подходит для такого периодического вызова.
Механизм длительных операций или фоновых заданий предназначен для выполнения ресурсоемких задач на сервере или в отдельном потоке, чтобы не блокировать интерфейс пользователя. Однако, как верно заметил автор, возникает вопрос о возврате результата на клиентскую форму для интерактивного отображения.
ВыполнитьОбработкуВФоне() или НачатьДлительнуюОперацию()).ДлительныеОперацииКлиент.ОжидатьЗавершение(), указывая процедуру-обработчик оповещения.Мы не будем приводить полный пример кода для длительных операций здесь, так как для интерактивного клиентского вебсокета ПодключитьОбработчикОжидания более нагляден, а полноценная реализация серверного вебсокет-прокси с длительными операциями – это достаточно объемная задача.
Асинх, Ждать, Обещание) (начиная с версии 8.3.18)С версии 8.3.18 платформа 1С:Предприятие ввела синтаксис для асинхронных операций, схожий с async/await из JavaScript. Этот подход позволяет писать асинхронный код более последовательно и читабельно, избегая "пирамиды" из вложенных обработчиков.
Асинх: Применяется к процедурам и функциям, которые будут содержать асинхронные вызовы.Ждать: Используется внутри асинхронных процедур/функций для приостановки их выполнения до завершения асинхронной операции, не блокируя при этом основной поток программы.Обещание: Представляет собой результат асинхронной операции, который будет доступен после ее завершения.Важно: Применимость этого подхода напрямую зависит от того, поддерживает ли ваша внешняя компонента для вебсокета асинхронные методы (например, ПринятьАсинх()) или является ли сама операция Клиент.Принять() достаточно быстрой, чтобы не задерживать выполнение, если она не вернет данные сразу. Если внешняя компонента предоставляет асинхронные интерфейсы, этот способ будет наиболее современным и удобным для организации работы с вебсокетом.
Если ваша внешняя компонента не имеет асинхронных методов, то использование Асинх/Ждать с блокирующими вызовами не даст полного асинхронного эффекта и может все равно привести к задержкам. Однако, мы можем использовать его в сочетании с ПодключитьОбработчикОжидания, чтобы асинхронно вызывать более крупные блоки логики, если компонента поддерживает что-то вроде НачатьПриемСообщения и ЗавершитьПриемСообщения.
Наиболее современным, эффективным и рекомендованным подходом для работы с WebSocket в 1С:Предприятии, если ваша версия платформы это позволяет, является использование нативной поддержки протокола, которая появилась начиная с версии 8.3.27.
Платформа 1С:Предприятие предоставляет новые встроенные объекты языка для работы с WebSocket, а также новый тип объектов метаданных "WebSocket-клиент". Это позволяет нам создавать и управлять WebSocket-соединениями непосредственно средствами 1С, без использования внешних компонент.
WebSocketКлиент: Используется для создания и управления WebSocket-соединением.ПриУстановкеСоединения: Срабатывает при успешном установлении соединения.ПриПолученииСообщения: Срабатывает, когда от сервера приходит новое сообщение. Это ключевой обработчик для нашей задачи.ПриЗакрытииСоединения: Срабатывает при закрытии соединения (как с нашей стороны, так и со стороны сервера).ПриОшибкеСоединения: Срабатывает при возникновении ошибки в соединении.ПодключитьОбработчикОжидания), мы получаем сообщения только тогда, когда они реально приходят. Это наиболее эффективный способ работы с WebSocket.Это наиболее производительное и удобное решение для вашего кейса, так как оно специально разработано для работы с протоколом WebSocket и идеально соответствует его событийной природе. Если ваша версия платформы позволяет использовать нативную поддержку, мы настоятельно рекомендуем рассмотреть этот вариант в первую очередь.
Комментарий из форума "вебСокет не так работает - куеты ты приобрел" указывает на глубокое непонимание или некорректное использование протокола WebSocket в исходном подходе. Протокол WebSocket по своей природе является асинхронным и событийно-ориентированным. Это означает, что клиент не должен постоянно опрашивать сервер на предмет новых данных (как это происходит с блокирующим циклом Пока Клиент.Принять(0, Данные) Цикл). Вместо этого, сервер отправляет данные клиенту, когда они появляются, и клиент должен быть готов их принять через специально разработанные для этого обработчики событий.
Использование блокирующего цикла Пока Клиент.Принять(0, Данные) Цикл, даже с минимальным таймаутом, превращает асинхронный протокол в полусинхронный "опрос", который нагружает систему и приводит к блокировке интерфейса. В идеале внешняя компонента для WebSocket должна была бы предоставлять методы с обработчиками событий (callback-функциями), которые срабатывают при получении нового сообщения. Если такая функциональность отсутствует, то использование ПодключитьОбработчикОжидания с проверкой Клиент.Принять() является компромиссным решением, имитирующим событийную модель, но по сути остающимся polling-ом.
Мы видим, что проблема блокировки интерфейса при работе с вебсокетом в 1С решаема несколькими способами. Выбор конкретного подхода зависит от версии вашей платформы и функциональности используемой внешней компоненты:
ПодключитьОбработчикОжидания (для версии 8.3.17 и ниже, или если внешняя компонента не имеет асинхронных методов): Это наиболее доступное и относительно простое решение для клиентской стороны, позволяющее периодически проверять наличие данных и обновлять форму без полной блокировки интерфейса. Мы рекомендуем этот способ как быстрый и эффективный старт.Асинх, Ждать) (начиная с 8.3.18, если внешняя компонента поддерживает асинхронные вызовы): Если ваша внешняя компонента для вебсокета предоставляет асинхронные методы, этот подход будет наиболее современным и удобным для написания читаемого неблокирующего кода.Прежде чем выбирать, мы советуем проверить версию вашей платформы и ознакомиться с документацией вашей внешней компоненты на предмет наличия асинхронных или событийных методов. Использование подходящего механизма позволит нам построить стабильное и отзывчивое приложение 1С, эффективно работающее с внешними источниками данных.