Почему не выводятся сообщения из фонового задания в обычном режиме и как это исправить?

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

Разработка и отладка фонового режима в среде «1С:Предприятие 8.3» часто преподносит неприятные сюрпризы. Типичная ситуация: программист пишет длительную обработку (например, групповое изменение реквизитов или проведение документов), запускает ее в режиме отладки — и все сообщения, сгенерированные методом СообщениеПользователю.Сообщить() в процедуре ПередЗаписью(), прекрасно выводятся на экран. Однако стоит запустить ту же самую обработку в обычном (пользовательском) режиме, как окно сообщений оказывается абсолютно пустым. Сообщения просто исчезают.

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

Выясняем причину: почему в режиме отладки всё работает?

Для того чтобы понять суть проблемы, нам нужно заглянуть во внутреннее устройство платформы «1С» и механизмы БСП. Метод ДлительныеОперации.ВыполнитьВФоне(), который повсеместно используется в современных конфигурациях, ведет себя принципиально по-разному в зависимости от режима запуска системы:

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

Именно из-за этой архитектурной особенности поведение в режиме отладки кардинально отличается от рабочего режима.

Ловушка метода ПолучитьСообщенияПользователю() и очистка буфера

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

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

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

Пошаговое решение: реализуем правильный вывод сообщений

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

Шаг 1. Запуск фонового задания

Сначала мы инициируем длительную операцию. В нашем примере мы будем использовать стандартные механизмы БСП. Нам важно правильно настроить параметры выполнения:


ПараметрыВыполнения = ДлительныеОперации.ПараметрыВыполненияПроцедуры();
ПараметрыВыполнения.ВыводитьПрогрессВыполнения = Истина;
ПараметрыВыполнения.ВыводитьСообщения = Ложь; // Мы будем управлять выводом сообщений вручную

РезультатОперации = ДлительныеОперации.ВыполнитьПроцедуру(
    "МойОбщийМодуль.ВыполнитьДлительнуюОбработку", 
    ПараметрыВыполнения
);

Шаг 2. Подключение обработчика ожидания на клиенте

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


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

ПодключитьОбработчикОжидания(
    "ПроверитьЗавершениеФоновогоЗадания", 
    ПараметрыОжидания.ТекущийИнтервал, 
    Истина
);

Шаг 3. Обработка очереди сообщений при опросе

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


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

Шаг 4. Получение данных на сервере

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


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

Альтернативный вариант: передача лога через временное хранилище

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

Рассмотрим, как устроен этот подход:

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

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

← На главную