Как заставить управляемое приложение 1С досрочно отображать сообщения пользователю

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

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

В данной статье мы разберем причины такого поведения платформы и рассмотрим несколько способов решения этой задачи: от официально рекомендованных до «хитрых» синхронных методов.

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

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

Решение 1: Использование фоновых заданий (Рекомендованный способ)

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

  1. Запуск фонового задания: Код обработки данных переносится в общий модуль с признаком «Сервер» и вызывается через ФоновыеЗадания.Выполнить().
  2. Механизм получения сообщений: На стороне клиента запускается обработчик ожидания (метод ПодключитьОбработчикОжидания()), который с периодичностью в 1–2 секунды опрашивает состояние задания.
  3. Чтение сообщений: Внутри обработчика ожидания мы используем метод задания ПолучитьСообщенияПользователю().

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

Решение 2: Использование метода «Состояние» для индикации прогресса

Если вам не нужен детальный лог из тысячи строк, а достаточно информировать пользователя о текущем этапе, проанализируем возможности метода Состояние(). В отличие от Сообщить(), метод Состояние() в клиентском коде может обновлять информацию в строке состояния чуть более оперативно, однако он всё равно подвержен общим правилам отрисовки интерфейса. Рассмотрим пример использования:


Для Счетчик = 1 По 100 Цикл
    // Выполняем полезную нагрузку
    Состояние("Обработка данных...", Счетчик, "Выполнено " + Счетчик + "%");
    // В клиентском коде это сработает эффективнее, чем Сообщить()
КонецЦикла;

Решение 3: Синхронный «хак» через обновление видимости элементов

Разберем ситуацию, когда нам принципиально важно оставаться в рамках одного потока выполнения (синхронно). Существует возможность заставить платформу выполнить неявный серверный вызов, который спровоцирует перерисовку формы и «выброс» накопленного буфера сообщений на экран. Один из таких способов — манипуляция свойством Видимость элементов формы.

Посмотрим на алгоритм реализации:

  1. Разместим на форме вспомогательный элемент (например, Группа или Поле).
  2. В цикле будем программно менять его видимость туда-обратно.

Для Счетчик = 1 По 20 Цикл
    Сообщить("Итерация №" + Счетчик);
    
    // Принудительная перерисовка
    Элементы.ВспомогательныйЭлемент.Видимость = Ложь;
    Элементы.ВспомогательныйЭлемент.Видимость = Истина;
    
    // Имитация длительной работы
    Для Счетчик2 = 1 По 100000 Цикл
        Пустышка = Счетчик2;
    КонецЦикла;
КонецЦикла;

Важное предостережение: Выясним, почему это решение называют «адским». Изменение видимости приводит к контекстному серверному вызову. Это означает, что на каждой итерации цикла весь контекст формы (все данные реквизитов) будет гоняться туда-обратно между клиентом и сервером. Кроме того, это приводит к очистке кэшей представлений ссылок и данных быстрого выбора. Используйте этот метод только в «легких» формах и с редкой периодичностью (например, раз в 10–50 итераций).

Решение 4: Поле HTML-документа для вывода лога

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

Разберем шаги реализации:

  1. Помещаем на форму поле ПолеЛога с типом Строка и видом Поле HTML документа.
  2. При инициализации загружаем в него базовую структуру страницы.
  3. В цикле вызываем выполнение скрипта, который добавляет строку в DOM-дерево.

// Пример вызова из 1С
ТекстСкрипта = "var p = document.createElement('p'); p.innerHTML = '" + ТекущееСообщение + "'; document.body.appendChild(p);";
Элементы.ПолеЛога.Документ.parentWindow.eval(ТекстСкрипта);

Поскольку управление HTML-документом происходит через прямое обращение к объектам COM (в Windows-клиенте) или внутренним методам браузера, это может позволить обновлять картинку без завершения основного серверного вызова 1С.

Решение 5: Метод ОтобразитьИзменениеДанных

Существует еще один метод — ОтобразитьИзменениеДанных(). Первоначально он предназначен для оповещения других открытых форм о том, что данные в базе изменились. Но, как заметили разработчики, его вызов также провоцирует клиент-серверную синхронизацию.


Сообщить(Счетчик);
ОтобразитьИзменениеДанных(СсылкаНаОбъект, ВидИзмененияДанных.Изменение);

Проанализируем последствия: данный метод также выполняет обращение к серверу и очищает кэши на клиенте. Его преимущество перед «передергиванием» видимости в том, что он выглядит чуть более легитимно в коде, но технические последствия для производительности будут схожими.

Подведем итоги

Мы выяснили, что штатного, легкого и синхронного метода для мгновенного вывода Сообщить() в управляемом приложении не существует в силу архитектурных особенностей платформы.

Рекомендации по выбору:

Помните, что избыточное количество сообщений (десятки тысяч строк) перегружает интерфейс и затрудняет работу пользователя. Хорошим тоном считается вывод только ключевых вех процесса или использование шкалы прогресса.

← На главную