При разработке обработок, выполняющих длительные операции, часто возникает задача информировать пользователя о ходе процесса. Стандартный подход — вывод сообщений в цикле с помощью процедуры Сообщить(). Однако многие разработчики сталкиваются с ситуацией, когда все сообщения появляются на экране одновременно, только после завершения всего цикла, создавая у пользователя впечатление, что программа «зависла». Разберем, почему так происходит и как добиться отображения прогресса в реальном времени.
Проанализируем типичную ситуацию. Есть клиентская процедура, которая в цикле обходит выборку документов и для каждого вызывает серверную процедуру обработки, после чего выводит сообщение.
&НаКлиенте
Процедура ПереподставитьДоговорСчетНаОплату(Команда)
выборкаДокументы = ЗапросСчетов(); // Предположим, что эта функция возвращает массив или ТЗ
для каждого впДокумент из выборкаДокументы цикл
ПереподставитьДоговорСчетНаОплатуНаСервере(впДокумент);
Сообщить(впДокумент) ;
конецЦикла;
КонецПроцедуры
Основная проблема заключается в том, что клиентский код, особенно с серверными вызовами внутри, выполняется синхронно. Это означает, что пока вся процедура ПереподставитьДоговорСчетНаОплату() не завершится, поток управления не вернется к интерфейсу пользователя. Система накапливает все вызовы Сообщить() в буфере и отображает их единым пакетом, когда получает возможность обновить экран, то есть после окончания цикла. Давайте рассмотрим несколько способов решения этой задачи.
Этот метод является самым прямым решением проблемы без использования сложных фоновых заданий. Идея состоит в том, чтобы разбить один большой монолитный цикл на множество маленьких, независимых вызовов. Каждый вызов будет обрабатывать один элемент, выводить сообщение, а затем планировать следующий вызов через короткий промежуток времени. Это дает интерфейсу «передышку» для обновления.
Разберем по шагам:
Массив или ТаблицаЗначений).Посмотрим на пример. Добавим на форму реквизит ДокументыДляОбработки (тип Массив).
&НаКлиенте
Процедура ВыполнитьОбработку(Команда)
// Шаг 1: Получаем все данные и сохраняем в реквизит формы
ДокументыДляОбработки = ЗапросСчетовНаСервере();
// Проверяем, есть ли что обрабатывать
Если ДокументыДляОбработки.Количество() > 0 Тогда
// Шаг 2: Запускаем обработчик в первый раз
ПодключитьОбработчикОжидания("ОбработатьОдинДокумент", 0.1, Истина);
Иначе
Сообщить("Нет документов для обработки!");
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Процедура ОбработатьОдинДокумент()
// Шаг 3: Обрабатываем один элемент
Если ДокументыДляОбработки.Количество() = 0 Тогда
// Список пуст, завершаем работу
ОтключитьОбработчикОжидания("ОбработатьОдинДокумент");
Сообщить("Обработка успешно завершена!");
Возврат;
КонецЕсли;
// Берем первый документ из массива
ТекущийДокумент = ДокументыДляОбработки[0];
// Выполняем серверный вызов
ПереподставитьДоговорСчетНаОплатуНаСервере(ТекущийДокумент);
// Выводим сообщение в реальном времени
Сообщить("Обработан: " + ТекущийДокумент);
// Удаляем обработанный документ из массива
ДокументыДляОбработки.Удалить(0);
// Шаг 4: Планируем следующий вызов (если остались документы)
Если ДокументыДляОбработки.Количество() > 0 Тогда
ПодключитьОбработчикОжидания("ОбработатьОдинДокумент", 0.1, Истина);
Иначе
// Это был последний документ, отключаемся
ОтключитьОбработчикОжидания("ОбработатьОдинДокумент");
Сообщить("Обработка успешно завершена!");
КонецЕсли;
КонецПроцедуры
Плюсы метода:
Минусы метода:
Это более современный и правильный подход для действительно долгих и ресурсоемких задач. Он заключается в том, чтобы перенести всю обработку в фоновое задание на сервере. В этом случае интерфейс пользователя остается полностью свободным и отзывчивым.
Стандартные конфигурации часто содержат подсистему для работы с длительными операциями (например, общий модуль ДлительныеОперации). Общая схема работы выглядит так:
Плюсы метода:
Минусы метода:
Вне зависимости от выбранного способа, стоит помнить о некоторых нюансах.
1. Сообщить() — это медленно.
Процедура Сообщить() предназначена для вывода важных уведомлений, а не для логирования процесса. Каждый ее вызов — это операция взаимодействия с подсистемой интерфейса, которая требует ресурсов. При обработке тысяч документов частые вызовы Сообщить() могут значительно замедлить общую скорость выполнения, иногда в несколько раз.
2. Используйте более подходящие элементы интерфейса.
Бесконечная «простыня» из сообщений в служебном окне неудобна для пользователя. Рассмотрите альтернативы:
Индикатор и обновляйте его значение после обработки каждого документа. Это гораздо нагляднее показывает прогресс.Надпись на форме. Например: «В работе: Счет на оплату №123 от 01.01.2023».Эти элементы обновляются так же, как и сообщения, и отлично работают в связке с обработчиком ожидания.
В итоге, выбор метода зависит от задачи. Для относительно быстрых операций (до минуты) и когда некритично, что пользователь не сможет работать в программе, отлично подойдет обработчик ожидания. Для тяжелых, долгих процессов, где важна отзывчивость интерфейса, единственно верным решением будет использование фоновых заданий.