ФоновыеЗадания.Выполнить: запуск кода в фоне, параметры, контроль и отладка

· 9 мин

Содержание

Программно фоновое задание в 1С 8.3 запускают на сервере методом ФоновыеЗадания.Выполнить("ОбщийМодуль.Процедура", МассивПараметров, Ключ, Наименование). Процедура должна быть экспортной и лежать в серверном общем модуле. Метод сразу возвращает объект ФоновоеЗадание, а код продолжает работу, не дожидаясь окончания.

Есть два пути. Голый вызов платформы подходит для простых задач: пересчитать регистр, выгрузить файл, отправить письма. В конфигурациях на БСП (Бухгалтерия 3.0, ЗУП 3.1, УТ 11) удобнее обёртка ДлительныеОперации.ВыполнитьВФоне: она сама передаёт результат в форму, показывает индикатор и корректно работает в файловой базе. Ниже оба варианта, с кодом, правами и разбором ошибок.

Как устроены фоновые задания в платформе 1С

Фоновое задание — это отдельный сеанс информационной базы без интерфейса. Он стартует по команде из кода, выполняет одну процедуру и завершается.

Чем фоновое задание отличается от обычного серверного вызова:

  • ⏱ Не блокирует пользователя. Форма остаётся доступной, можно показывать прогресс.
  • 🔀 Работает параллельно. Несколько заданий идут одновременно в разных сеансах — так ускоряют длинные обработки, разбив данные на порции.
  • 🚫 Нет интерфейса. Формы, Вопрос(), Предупреждение() и любые клиентские методы недоступны.
  • 👤 Работает от имени пользователя, который его запустил, с его ролями.

Фоновые задания путают с регламентными. Разница простая:

ЧтоКто запускаетГде описаноКогда использовать
Фоновое заданиеВаш код, в любой моментНигде, только вызов методаРазовые долгие операции по команде
Регламентное заданиеПлатформа по расписаниюОбъект метаданных «Регламентное задание»Периодические операции: обмен, закрытие, загрузка курсов

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

Клиент-серверный и файловый вариант

В клиент-серверной базе задание выполняет рабочий процесс сервера 1С. Пользователь может закрыть программу — задание доработает.

В файловой базе отдельного сервера нет. Задания выполняет процесс приложения, которое открыло базу: тонкий или толстый клиент либо модуль веб-сервера при публикации. Закрыли программу — незавершённое задание оборвалось. Для долгих операций в файловой базе это нужно учитывать и предупреждать пользователя.

Синтаксис ФоновыеЗадания.Выполнить и его параметры

Метод доступен на сервере, в толстом клиенте и во внешнем соединении. Из модуля формы его вызывают через серверную процедуру с директивой &НаСервере.

ФоновыеЗадания.Выполнить(<ИмяМетода>, <Параметры>, <Ключ>, <Наименование>)
ПараметрТипОбязательныйЧто передавать
ИмяМетодаСтрокаДа"ИмяМодуля.ИмяПроцедуры" — экспортная процедура серверного общего модуля
ПараметрыМассивНетЗначения параметров процедуры по порядку
КлючСтрокаНетЗащита от повторного запуска одного и того же
НаименованиеСтрокаНетПонятное имя для списка заданий и журнала

Минимальный рабочий пример. Общий модуль ФоновыеОбработки с флажком «Сервер», флажок «Глобальный» снят:

// Общий модуль ФоновыеОбработки
Процедура ПересчитатьОстатки(ДатаНачала, ДатаОкончания) Экспорт

    ЗаписьЖурналаРегистрации("Пересчёт остатков",
        УровеньЖурналаРегистрации.Информация, , ,
        СтрШаблон("Период %1 – %2", Формат(ДатаНачала, "ДФ=dd.MM.yyyy"),
            Формат(ДатаОкончания, "ДФ=dd.MM.yyyy")));

    // ... основная работа

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

Запуск из формы:

&НаСервере
Процедура ЗапуститьПересчётНаСервере()

    Параметры = Новый Массив;
    Параметры.Добавить(Объект.ДатаНачала);
    Параметры.Добавить(Объект.ДатаОкончания);

    Задание = ФоновыеЗадания.Выполнить(
        "ФоновыеОбработки.ПересчитатьОстатки",
        Параметры,
        "ПересчётОстатков",
        "Пересчёт остатков за период");

    ИдентификаторЗадания = Задание.УникальныйИдентификатор;

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

ИдентификаторЗадания — реквизит формы типа УникальныйИдентификатор. По нему потом проверяют состояние задания.

Ключ работает так: пока активно задание с тем же методом и тем же ключом, второй запуск вызывает исключение. Это готовая защита от двойного нажатия кнопки. Чтобы ограничить одно задание на документ, добавьте в ключ его уникальный идентификатор: "Проведение_" + Строка(Ссылка.УникальныйИдентификатор()).

Процедура задания: передача параметров и возврат результата

Количество элементов массива Параметры должно совпадать с числом параметров процедуры. Порядок тот же, что в объявлении.

Что можно передавать

Задание стартует в другом сеансе, поэтому значения передаются копией. Безопасно передавать:

  • ✅ примитивные типы: строки, числа, даты, булево;
  • ✅ ссылки на объекты базы;
  • ✅ структуры и массивы из таких значений;
  • ✅ адрес во временном хранилище — строкой.

Не передавайте:

  • ❌ данные формы и сами формы;
  • ❌ менеджер временных таблиц;
  • ❌ объекты, которые нельзя сериализовать (запрос с открытым результатом, COM-объекты).

Большую таблицу значений проще положить во временное хранилище и передать в задание только адрес.

Как вернуть результат

Процедура фонового задания ничего не возвращает в вызывающий код. Результат кладут во временное хранилище. Адрес заранее создают на стороне формы и передают параметром:

&НаСервере
Процедура ЗапуститьОтчётНаСервере()

    АдресРезультата = ПоместитьВоВременноеХранилище(Неопределено, УникальныйИдентификатор);

    Параметры = Новый Массив;
    Параметры.Добавить(Объект.Организация);
    Параметры.Добавить(АдресРезультата);

    Задание = ФоновыеЗадания.Выполнить("ФоновыеОбработки.СобратьДанные", Параметры, , "Сбор данных");
    ИдентификаторЗадания = Задание.УникальныйИдентификатор;

КонецПроцедуры
// Общий модуль ФоновыеОбработки
Процедура СобратьДанные(Организация, АдресРезультата) Экспорт

    Запрос = Новый Запрос;
    Запрос.Текст = "ВЫБРАТЬ
    |    Контрагенты.Ссылка КАК Контрагент
    |ИЗ
    |    Справочник.Контрагенты КАК Контрагенты
    |ГДЕ
    |    НЕ Контрагенты.ПометкаУдаления";

    Таблица = Запрос.Выполнить().Выгрузить();
    ПоместитьВоВременноеХранилище(Таблица, АдресРезультата);

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

После завершения задания форма забирает таблицу через ПолучитьИзВременногоХранилища(АдресРезультата). Хранилище привязано к уникальному идентификатору формы и живёт, пока форма открыта.

Обёртка БСП: ДлительныеОперации

В типовых конфигурациях тот же сценарий короче. Процедура пишется с двумя параметрами — Параметры и АдресРезультата, — а запуск и ожидание берёт на себя библиотека:

&НаСервере
Функция ЗапуститьНаСервере()

    ПараметрыВыполнения = ДлительныеОперации.ПараметрыВыполненияВФоне(УникальныйИдентификатор);
    ПараметрыВыполнения.НаименованиеФоновогоЗадания = "Сбор данных";

    ПараметрыПроцедуры = Новый Структура("Организация", Объект.Организация);

    Возврат ДлительныеОперации.ВыполнитьВФоне("ФоновыеОбработки.СобратьДанныеБСП",
        ПараметрыПроцедуры, ПараметрыВыполнения);

КонецФункции

&НаКлиенте
Процедура Сформировать(Команда)

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

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

&НаКлиенте
Процедура ПослеЗавершения(Результат, ДополнительныеПараметры) Экспорт

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

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

Состав полей структуры Результат зависит от версии БСП. Сверьте его с описанием функции ОжидатьЗавершение в общем модуле ДлительныеОперацииКлиент вашей конфигурации — комментарий над функцией перечисляет все поля.

Свои общие модули не добавляйте прямо в типовую конфигурацию. Положите их в расширение: при обновлении релиза доработка не потеряется, а обёртка БСП из расширения вызывается так же.

Права и роли для фоновой обработки

Отдельного права «запускать фоновые задания» в платформе нет. Задание получает права пользователя, который его запустил:

  • 🔑 Если у пользователя нет права чтения регистра, процедура задания упадёт на первом же запросе к нему.
  • 🔑 Ограничения доступа на уровне записей (RLS) действуют и в фоне.
  • 🔑 Параметры сеанса в фоновом сеансе устанавливаются заново, через модуль сеанса.

Когда процедура должна сделать больше, чем разрешено пользователю, есть два приёма.

  1. Привилегированный режим в коде. В начале процедуры УстановитьПривилегированныйРежим(Истина), в конце — Ложь. Режим действует только на сервере.
  2. Привилегированный общий модуль. В свойствах модуля флажок «Привилегированный». Весь код модуля выполняется без проверки прав.

Привилегированный режим отключает все проверки прав и RLS. Не передавайте в такую процедуру произвольные строки запроса или имена объектов от пользователя: это готовая дыра в доступе.

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

Мониторинг состояния и управление заданиями

Проверка из кода

Найти задание по идентификатору и узнать его состояние:

&НаСервереБезКонтекста
Функция СостояниеЗадания(Идентификатор)

    Задание = ФоновыеЗадания.НайтиПоУникальномуИдентификатору(Идентификатор);
    Если Задание = Неопределено Тогда
        Возврат "НеНайдено";
    КонецЕсли;

    Если Задание.Состояние = СостояниеФоновогоЗадания.Активно Тогда
        Возврат "Активно";
    ИначеЕсли Задание.Состояние = СостояниеФоновогоЗадания.Завершено Тогда
        Возврат "Завершено";
    ИначеЕсли Задание.Состояние = СостояниеФоновогоЗадания.ЗавершеноАварийно Тогда
        Возврат ПодробноеПредставлениеОшибки(Задание.ИнформацияОбОшибке);
    Иначе
        Возврат "Отменено";
    КонецЕсли;

КонецФункции

На клиенте эту функцию опрашивают через ПодключитьОбработчикОжидания("ПроверитьЗадание", 2, Ложь) и отключают обработчик, когда задание перестало быть активным. Опрос раз в 1–2 секунды достаточен: частые серверные вызовы сами создают нагрузку.

Полезные свойства и методы объекта ФоновоеЗадание:

Свойство или методЧто даёт
СостояниеАктивно, Завершено, ЗавершеноАварийно, Отменено
Начало, КонецВремя старта и окончания
ИнформацияОбОшибкеОписание исключения при аварийном завершении
ПолучитьСообщенияПользователю(Истина)Сообщения, выведенные в задании через Сообщение.Сообщить()
Отменить()Остановить задание
ОжидатьЗавершенияВыполнения(Таймаут)Ждать окончания на сервере, в секундах

Список заданий с отбором — ФоновыеЗадания.ПолучитьФоновыеЗадания(Новый Структура("Ключ, Состояние", "ПересчётОстатков", СостояниеФоновогоЗадания.Активно)). Внутри самого задания ссылку на себя даёт ПолучитьТекущийСеансИнформационнойБазы().ПолучитьФоновоеЗадание().

Параллельный запуск порциями

Длинную обработку можно разбить: например, 5 заданий по 2 000 документов вместо одного на 10 000. Запустите задания в цикле, сложите объекты в массив и дождитесь всех сразу:

Задания = Новый Массив;
Для Каждого Порция Из Порции Цикл
    Параметры = Новый Массив;
    Параметры.Добавить(Порция);
    Задания.Добавить(ФоновыеЗадания.Выполнить("ФоновыеОбработки.ОбработатьПорцию", Параметры));
КонецЦикла;

ФоновыеЗадания.ОжидатьЗавершенияВыполнения(Задания);

Число параллельных заданий подбирайте под сервер. Больше потоков, чем ядер у рабочих процессов, выигрыша не даст, а риск блокировок на одних и тех же таблицах вырастет.

Где посмотреть задания без кода

  • 🖥 В типовой конфигурации: Администрирование → Обслуживание → Регламентные операции → Регламентные и фоновые задания, вкладка с фоновыми заданиями. Там видны состояние, время, текст ошибки, и задание можно отменить.
  • 🖥 В консоли администрирования кластера: фоновые задания показаны в списке сеансов базы как отдельные сеансы. Отсюда сеанс можно завершить.
  • 📒 В журнале регистрации: события группы «Фоновое задание» — запуск, успешное завершение, ошибка, отмена.

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

Отладка фоновых заданий и типичные ошибки

Как поймать точку останова

Обычная точка останова в процедуре задания не срабатывает: код идёт в другом сеансе. Порядок действий:

  1. В Конфигураторе откройте Отладка → Подключение.
  2. Нажмите кнопку автоматического подключения и отметьте тип «Фоновое задание».
  3. Поставьте точку останова в процедуре общего модуля и запустите задание.

Для клиент-серверной базы сервер 1С должен быть запущен с отладкой — без этого отладчик к серверным сеансам не подключится.

Приём попроще на время разработки: вызовите ту же процедуру напрямую в серверной процедуре формы, без ФоновыеЗадания.Выполнить. Логика отлаживается как обычный серверный код, а в фон переводится одной строкой.

Разбор ошибок

Диагностика: выберите симптом

Ещё две ошибки, которые не дают исключения, но портят данные:

  • 🔁 Задание запускают в цикле без ключа. Пользователь дважды нажал кнопку — два задания параллельно пишут одни и те же документы. Ключ решает это одной строкой.
  • 🧹 Нет записи в журнал. Если задание упало ночью, через неделю причину уже не восстановить. Пишите ЗаписьЖурналаРегистрации в начале, в конце и в обработке исключения.

Частые вопросы

Можно ли запустить фоновое задание прямо с клиента?
Нет. В тонком и веб-клиенте метод недоступен. Вызовите серверную процедуру формы с директивой &НаСервере и уже из неё — ФоновыеЗадания.Выполнить.
Можно ли вызвать в фоне метод модуля менеджера документа?
Напрямую платформа принимает имя метода общего модуля. Сделайте в серверном общем модуле экспортную процедуру-обёртку, которая вызывает нужный метод менеджера.
Сколько живёт информация о завершённом задании?
Ограниченное время после окончания, затем платформа удаляет её из списка. Если результат нужен позже, сохраняйте его в базу или регистр сведений, а не полагайтесь на объект задания.
Запускает ли фоновое задание другое фоновое задание?
Да, из процедуры задания можно вызвать ФоновыеЗадания.Выполнить. Так делают диспетчер, который раздаёт порции дочерним заданиям и ждёт их завершения.
Есть ли у фонового задания ограничение по времени?
Встроенного таймаута нет: задание работает, пока не закончит процедуру, не будет отменено или пока не завершат его сеанс. Ограничение времени при необходимости делайте в коде, проверяя прошедшее время между порциями.