Разработка и отладка фонового режима в среде «1С:Предприятие 8.3» часто преподносит неприятные сюрпризы. Типичная ситуация: программист пишет длительную обработку (например, групповое изменение реквизитов или проведение документов), запускает ее в режиме отладки — и все сообщения, сгенерированные методом СообщениеПользователю.Сообщить() в процедуре ПередЗаписью(), прекрасно выводятся на экран. Однако стоит запустить ту же самую обработку в обычном (пользовательском) режиме, как окно сообщений оказывается абсолютно пустым. Сообщения просто исчезают.
В этой статье мы подробно разберем, почему так происходит, выясним внутренние механизмы платформы и Библиотеки стандартных подсистем (БСП), а также шаг за шагом реализуем надежный механизм трансляции сообщений из фонового задания на клиентский экран.
Для того чтобы понять суть проблемы, нам нужно заглянуть во внутреннее устройство платформы «1С» и механизмы БСП. Метод ДлительныеОперации.ВыполнитьВФоне(), который повсеместно используется в современных конфигурациях, ведет себя принципиально по-разному в зависимости от режима запуска системы:
Сообщить() напрямую попадают в локальное окно сообщений и отображаются на экране после завершения алгоритма.Именно из-за этой архитектурной особенности поведение в режиме отладки кардинально отличается от рабочего режима.
Платформа предоставляет стандартный метод для получения накопленных фоновым заданием сообщений: ФоновоеЗадание.ПолучитьСообщенияПользователю(УдалятьПолученные). Здесь кроется вторая главная ошибка разработчиков.
Параметр УдалятьПолученные определяет, будут ли прочитанные сообщения удалены из буфера фонового задания. По умолчанию (или если вы передаете туда значение Истина) платформа очищает очередь сообщений после чтения. Если БСП или ваши собственные механизмы мониторинга (например, при получении прогресса выполнения через СообщитьПрогресс) уже один раз вызвали этот метод с флагом удаления, то при финальном запросе по окончании работы задания массив сообщений окажется абсолютно пустым.
Поэтому сообщения необходимо считывать порциями на каждом шаге проверки состояния фонового задания и накапливать их в промежуточном массиве на клиенте.
Давайте по шагам разберем, как построить надежную схему запуска фонового задания и трансляции его сообщений пользователю.
Сначала мы инициируем длительную операцию. В нашем примере мы будем использовать стандартные механизмы БСП. Нам важно правильно настроить параметры выполнения:
ПараметрыВыполнения = ДлительныеОперации.ПараметрыВыполненияПроцедуры();
ПараметрыВыполнения.ВыводитьПрогрессВыполнения = Истина;
ПараметрыВыполнения.ВыводитьСообщения = Ложь; // Мы будем управлять выводом сообщений вручную
РезультатОперации = ДлительныеОперации.ВыполнитьПроцедуру(
"МойОбщийМодуль.ВыполнитьДлительнуюОбработку",
ПараметрыВыполнения
);
Поскольку фоновое задание выполняется асинхронно, клиентское приложение должно регулярно опрашивать сервер, проверять статус выполнения и забирать накопившиеся сообщения. Для этого мы используем обработчик ожидания.
// В клиентском модуле формы
ПараметрыОжидания = ДлительныеОперацииКлиент.ПараметрыОжидания(ЭтотОбъект);
ПараметрыОжидания.Вставить("ИдентификаторЗадания", РезультатОперации.ИдентификаторЗадания);
ПараметрыОжидания.Вставить("АдресРезультата", РезультатОперации.АдресРезультата);
ПараметрыОжидания.Вставить("НакопленныеСообщения", Новый Массив);
ПодключитьОбработчикОжидания(
"ПроверитьЗавершениеФоновогоЗадания",
ПараметрыОжидания.ТекущийИнтервал,
Истина
);
Теперь опишем саму процедуру обработчика ожидания. На каждом такте опроса мы будем обращаться к серверу, считывать новые сообщения и выводить их пользователю по мере поступления.
&НаКлиенте
Процедура ПроверитьЗавершениеФоновогоЗадания()
// Вызываем серверный метод проверки состояния задания
СостояниеЗадания = ПолучитьСостояниеИСообщенияНаСервере(
ПараметрыОжидания.ИдентификаторЗадания
);
// Выводим полученные сообщения на экран
Для Каждого Сообщение Из СостояниеЗадания.Сообщения Цикл
Сообщение.Сообщить();
КонецЦикла;
Если СостояниеЗадания.Статус = "Выполняется" Тогда
// Корректируем интервал опроса и перезапускаем обработчик
ДлительныеОперацииКлиент.ОбновитьПараметрыОбработчикаОжидания(ПараметрыОжидания);
ПодключитьОбработчикОжидания(
"ПроверитьЗавершениеФоновогоЗадания",
ПараметрыОжидания.ТекущийИнтервал,
Истина
);
ИначеЕсли СостояниеЗадания.Статус = "Выполнено" Тогда
ПоказатьПредупреждение(, "Обработка успешно завершена!");
Иначе
ВызватьИсключение "Ошибка выполнения фонового задания: " + СостояниеЗадания.ИмяИсключения;
КонецЕсли;
КонецПроцедуры
На стороне сервера нам нужно найти фоновое задание по его уникальному идентификатору, проверить его текущее состояние и, самое главное, получить порцию новых сообщений с флагом удаления Истина, чтобы не читать их повторно при следующем такте.
&НаСервереБезКонтекста
Функция ПолучитьСостояниеИСообщенияНаСервере(Знач ИдентификаторЗадания)
Результат = Новый Структура;
Результат.Вставить("Статус", "Выполняется");
Результат.Вставить("Сообщения", Новый Массив);
Результат.Вставить("ИмяИсключения", "");
Задание = ФоновыеЗадания.НайтиПоУникальномуИдентификатору(ИдентификаторЗадания);
Если Задание = Неопределено Тогда
Результат.Статус = "Ошибка";
Результат.ИмяИсключения = "Фоновое задание не найдено.";
Возврат Результат;
КонецЕсли;
// Считываем сообщения с удалением прочитанных
МассивСообщенийПлатформы = Задание.ПолучитьСообщенияПользователю(Истина);
Если МассивСообщенийПлатформы <> Неопределено Тогда
Результат.Сообщения = МассивСообщенийПлатформы;
КонецЕсли;
// Проверяем статус выполнения задания
Если Задание.Состояние = СостояниеФоновогоЗадания.Активно Тогда
Результат.Статус = "Выполняется";
ИначеЕсли Задание.Состояние = СостояниеФоновогоЗадания.Завершено Тогда
Результат.Статус = "Выполнено";
ИначеЕсли Задание.Состояние = СостояниеФоновогоЗадания.ЗавершеноАварийно Тогда
Результат.Статус = "Ошибка";
Если Задание.ИнформацияОбОшибке <> Неопределено Тогда
Результат.ИмяИсключения = Задание.ИнформацияОбОшибке.Описание;
Иначе
Результат.ИмяИсключения = "Неизвестная ошибка фонового задания.";
КонецЕсли;
КонецЕсли;
Возврат Результат;
КонецФункции
Хотя механизм трансляции сообщений через СообщениеПользователю полностью рабочий, он имеет свои ограничения (например, лимиты на объем передаваемого текста и отсутствие строгой структуры данных). Если вам нужно вернуть пользователю подробный отчет об обработанных документах, ошибках проведения или детальную лог-таблицу, профессиональные разработчики рекомендуют использовать Временное хранилище.
Рассмотрим, как устроен этот подход:
АдресРезультата.Этот способ гарантирует, что ни одно сообщение не потеряется из-за сброса буферов или непредвиденных исключительных ситуаций на сервере.