Как правильно работать с фоновыми заданиями в 1С 8.3: запуск, контроль и получение результатов?

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

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

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

Основные подходы к запуску фоновых заданий

В 1С существует два основных способа запуска кода в фоновом режиме:

  1. Регламентные задания: Используются для периодического, автоматического выполнения заранее определенного кода по расписанию. Они настраиваются в конфигурации и запускаются платформой (внешний регламент позволяет управлять ими более гибко).
  2. Динамические фоновые задания: Запускаются программно по требованию пользователя или в ответ на какое-либо событие. Именно этот вариант мы и будем рассматривать в рамках данной статьи, поскольку он отвечает нашему вопросу о неблокирующем выполнении конкретной операции.

Запуск динамического фонового задания с контролем завершения

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

Процесс выглядит следующим образом:

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

Рассмотрим пример кода, который демонстрирует этот подход. Предположим, у нас есть длительная операция по обработке товаров, которую мы хотим выполнить в фоне.

Представим, что у нас есть общий модуль на сервере (например, МойМодульСервер), где находится процедура, выполняющая длительную обработку:


// В ОбщийМодуль.МойМодульСервер
// Убедимся, что модуль экспортный и на сервере
&НаСервере
Процедура ДлительнаяОбработкаТоваров(ПараметрыОбработки) Экспорт

    // Проверяем, что параметры переданы корректно
    Если ПараметрыОбработки = Неопределено Тогда
        ЗаписатьВЖурналРегистрации("ДлительнаяОбработкаТоваров", УровеньЖурналаРегистрации.Ошибка, , , "Переданы некорректные параметры обработки.");
        Возврат Неопределено; // Можно вернуть ошибку или пустой результат
    КонецЕсли;

    // Предположим, что ПараметрыОбработки - это структура с ключом "СписокТоваров"
    СписокТоваровДляОбработки = ПараметрыОбработки.СписокТоваров;
    ИтоговыйРезультат = 0;

    Попытка
        Для Каждого ТоварИзСписокТоваровДляОбработки Цикл
            // Имитация длительной операции
            // Например, пересчет остатков, обновление цен, сложные вычисления
            Для ИНД = 1 По 100000 Цикл
                // Некоторые вычисления
            КонецЦикла;
            ИтоговыйРезультат = ИтоговыйРезультат + 1; // Просто для примера
        КонецЦикла;

        Возврат ИтоговыйРезультат; // Возвращаем результат обработки
    Исключение
        ЗаписатьВЖурналРегистрации("ДлительнаяОбработкаТоваров", УровеньЖурналаРегистрации.Ошибка, , , ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
        ВызватьИсключение "Ошибка выполнения длительной обработки: " + ОписаниеОшибки();
    КонецПопытки;

КонецПроцедуры

Теперь рассмотрим, как запустить эту серверную процедуру из клиентского кода и получить результат:


&НаКлиенте
Процедура ЗапуститьФоновоеЗаданиеНаОбработку(Команда)

    // 1. Подготовка параметров для передачи в фоновое задание
    // Важно: Параметры должны быть сериализуемыми (ссылки, примитивные типы, структуры, массивы из них)
    СписокВыбранныхТоваров = Новый Массив;
    // ... Заполняем СписокВыбранныхТоваров, например, из таблицы формы
    // Пример: СписокВыбранныхТоваров.Добавить(Справочники.Номенклатура.НайтиПоНаименованию("Монитор"));
    //         СписокВыбранныхТоваров.Добавить(Справочники.Номенклатура.НайтиПоНаименованию("Клавиатура"));
    // Предположим, что мы получили список из какого-то реквизита формы
    Если Объект.СписокТоваров.Количество() > 0 Тогда
        Для Каждого Строка Из Объект.СписокТоваров Цикл
            Если Строка.Товар.Пустая() Тогда
                Продолжить;
            КонецЕсли;
            СписокВыбранныхТоваров.Добавить(Строка.Товар);
        КонецЦикла;
    Иначе
        Сообщить("Выберите товары для обработки.", СтатусСообщения.Важное);
        Возврат;
    КонецЕсли;

    ПараметрыДляФоновогоЗадания = Новый Структура;
    ПараметрыДляФоновогоЗадания.Вставить("СписокТоваров", СписокВыбранныхТоваров);

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

    // 3. Запуск фонового задания
    // Первый параметр: ОповещениеОЗавершении
    // Второй параметр: Строка - полное имя метода на сервере (Модуль.Процедура)
    // Третий параметр: Произвольный - параметры, которые будут переданы в серверный метод
    // Четвертый параметр: Булево - ожидать завершения или нет (обычно Ложь, чтобы не блокировать)
    ВыполнитьОбработкуВФоне(ОписаниеОповещения, "ОбщийМодуль.МойМодульСервер.ДлительнаяОбработкаТоваров", ПараметрыДляФоновогоЗадания, Ложь);

    Сообщить("Фоновое задание по обработке товаров запущено. Ожидайте уведомления о завершении.");

КонецПроцедуры

// Процедура-обработчик оповещения, которая будет вызвана после завершения фонового задания
&НаКлиенте
Процедура ОбработатьРезультатДлительнойОбработки(Результат, ДополнительныеПараметры) Экспорт

    // Результат - это структура, содержащая информацию о выполнении фонового задания:
    //  - Статус: СтатусВыполненияФоновогоЗадания (Выполнено, Ошибка, Отменено и т.д.)
    //  - Результат: Значение, возвращенное серверной процедурой
    //  - ОписаниеОшибки: Строка с текстом ошибки, если Статус = Ошибка
    //  - ИдентификаторФоновогоЗадания: Уникальный идентификатор запущенного задания

    Сообщить("Фоновое задание завершено. Идентификатор: " + ДополнительныеПараметры.ИдентификаторЗадания);

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

КонецПроцедуры

Передача параметров в фоновое задание

При передаче параметров в фоновое задание через третий аргумент функции ВыполнитьОбработкуВФоне, необходимо учитывать, что эти параметры должны быть сериализуемыми. Это означает, что платформа должна иметь возможность преобразовать их в формат, пригодный для передачи между процессами, а затем восстановить. К сериализуемым типам относятся:

Чего нельзя передавать напрямую:

  1. Объекты форм и их элементы (например, ЭлементыФормы).
  2. Объекты клиентской файловой системы (например, Файл).
  3. Некоторые COM-объекты или объекты, созданные вне контекста 1С-сервера.
  4. Открытые соединения или транзакции, которые должны быть закрыты в том же контексте, где были открыты.

Если вам нужно передать сложный объект, который не является сериализуемым, или же большой объем данных, рекомендуется использовать следующие подходы:

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

Пример использования временного хранилища:


&НаКлиенте
Процедура ЗапуститьСБольшимиДанными(Команда)

    БольшаяТаблицаДанных = Новый ТаблицаЗначений;
    БольшаяТаблицаДанных.Колонки.Добавить("Код");
    БольшаяТаблицаДанных.Колонки.Добавить("Наименование");
    // ... заполняем БольшуюТаблицуДанных тысячами строк ...
    Для ИНД = 1 По 10000 Цикл
        НоваяСтрока = БольшаяТаблицаДанных.Добавить();
        НоваяСтрока.Код = "Код" + ИНД;
        НоваяСтрока.Наименование = "Наименование" + ИНД;
    КонецЦикла;

    // Помещаем таблицу во временное хранилище и получаем ее адрес
    АдресХранилища = ПоместитьВоВременноеХранилище(БольшаяТаблицаДанных);

    ПараметрыДляФоновогоЗадания = Новый Структура;
    ПараметрыДляФоновогоЗадания.Вставить("АдресДанных", АдресХранилища);

    ОписаниеОповещения = Новый ОписаниеОповещения("ОбработатьРезультатОбработкиБольшихДанных", ЭтотОбъект);

    ВыполнитьОбработкуВФоне(ОписаниеОповещения, "ОбщийМодуль.МойМодульСервер.ОбработатьБольшиеДанные", ПараметрыДляФоновогоЗадания, Ложь);

    Сообщить("Фоновое задание с большими данными запущено.");

КонецПроцедуры

// В ОбщийМодуль.МойМодульСервер
&НаСервере
Процедура ОбработатьБольшиеДанные(Параметры) Экспорт

    // Получаем адрес хранилища из параметров
    АдресХранилища = Параметры.АдресДанных;

    // Получаем данные из временного хранилища по адресу
    БольшаяТаблицаДанных = ПолучитьИзВременногоХранилища(АдресХранилища);

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

КонецПроцедуры

Хранение результатов фоновых заданий

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

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

  1. Временное хранилище: Аналогично передаче параметров, большой результат можно поместить во временное хранилище на сервере (ПоместитьВоВременноеХранилище) и вернуть клиенту только адрес этого хранилища. На клиенте по этому адресу мы сможем получить результат.
  2. Специализированные объекты базы данных: Мы можем создать в конфигурации специальные объекты (например, регистр сведений или отдельный справочник/документ), предназначенные для хранения результатов длительных операций. Фоновое задание записывает свои результаты в эти объекты, а клиентская часть затем считывает их. Это удобно, если результаты нужно хранить постоянно или если требуется более сложная аналитика по результатам выполненных заданий.
  3. Создание файла на сервере: Если результатом является файл (например, Excel-отчет), фоновое задание может сформировать этот файл на сервере (например, во временном каталоге) и вернуть клиенту путь к нему или адрес временного хранилища с бинарными данными файла. Клиент затем может предложить пользователю скачать этот файл.

Мониторинг и отладка фоновых заданий

Контролировать состояние фоновых заданий и отлаживать их не менее важно, чем запускать. Разберем основные инструменты:

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

Важные аспекты и особенности работы с фоновыми заданиями

При использовании фоновых заданий необходимо помнить о нескольких ключевых моментах:

  1. Права доступа: Фоновое задание выполняется от имени пользователя, который его запустил (если это не регламентное задание). Поэтому у этого пользователя должны быть достаточные права на выполнение всех операций, которые происходят внутри фонового задания (чтение, запись, изменение данных, вызов определенных методов).
  2. Отсутствие интерактивных объектов: Внутри фонового задания нет доступа к интерактивным объектам клиентского приложения (формам, элементам управления, диалоговым окнам, методам Вопрос(), Предупреждение()). Весь код должен быть серверным и неинтерактивным. Любые сообщения или результаты должны передаваться обратно на клиент через механизм оповещений или через журнал регистрации.
  3. Блокировки: Несмотря на то, что фоновое задание не блокирует клиентский интерфейс, оно может блокировать данные в базе данных, если выполняет операции записи. Необходимо тщательно продумать логику работы с данными, чтобы минимизировать время блокировок и избежать конфликтов с интерактивной работой пользователей.
  4. Управление длительностью: Длительные операции могут потреблять значительные ресурсы сервера. Мы можем устанавливать максимальное время выполнения фонового задания (хотя для динамических это сложнее, чем для регламентных), а также контролировать его выполнение через консоль. Важно обеспечить возможность отмены задания, если оно зависло или выполняется слишком долго.
  5. Состояние фонового задания: Внутри серверной процедуры фонового задания мы можем обновлять его состояние и процент выполнения с помощью методов объекта ФоновоеЗаданиеТекущее. Это позволяет отображать прогресс выполнения на клиенте.
    
    // В серверной процедуре фонового задания
    Если ФоновыеЗадания.ТекущееФоновоеЗадание() <> Неопределено Тогда
        ФоновыеЗадания.ТекущееФоновоеЗадание().УстановитьПроцентВыполнения(50);
        ФоновыеЗадания.ТекущееФоновоеЗадание().УстановитьСостояние("Обработана половина данных.");
    КонецЕсли;
    

    Эти данные затем можно получить на клиенте с помощью ПолучитьФоновыеЗадания().

Заключение

Работа с фоновыми заданиями в 1С 8.3 — это мощный инструмент для повышения производительности и удобства использования системы. Мы рассмотрели основные механизмы запуска, получения результатов через ОписаниеОповещения, особенности передачи параметров, способы хранения объемных результатов, а также методы мониторинга и отладки. Помните о сериализуемости данных, правах доступа и неинтерактивности серверного кода. Используя эти знания, мы можем эффективно проектировать и реализовывать сложные бизнес-процессы, не нарушая работу пользователей.

← На главную