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