Как выполнить код в расширении ПОСЛЕ завершения фонового задания, запущенного командой формы

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

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

Почему стандартное расширение команды не работает для фоновых заданий

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

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

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

Решение 1: Расширение процедуры обработки оповещения (Callback)

Это самый правильный и надежный метод. Нам нужно найти не саму команду, а ту процедуру, которая указана в качестве обработчика завершения в объекте ОписаниеОповещения. Выясним, как это сделать.

Разберем пример типичного кода команды, которую нам нужно расширить:


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

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

В данном случае расширять нужно не КомандаВыполнитьРассчитать, а процедуру ПослеЗавершенияРассчета. Именно в ней мы применяем аннотацию &После. Посмотрим на код расширения:


&После("ПослеЗавершенияРассчета")
Процедура Расш1_ПослеЗавершенияРассчета(Результат, ДополнительныеПараметры)
    
    // Наш код выполнится строго после того, как отработает стандартная логика
    // обработки результата фонового задания.
    Сообщить("Все изменения применены, выполняем дополнительную логику!");
    
КонецПроцедуры

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

Решение 2: Подмена ОписанияОповещения через &Вместо

Иногда процедура завершения является общей или вызывается динамически, и прямое расширение &После применить сложно. В такой ситуации мы можем применить метод "перехвата" самого описания оповещения. Проанализируем, как это реализовать через аннотацию &Вместо на саму команду.

Рассмотрим алгоритм действий:

  1. Используем &Вместо для процедуры команды формы.
  2. Копируем оригинальный код команды в расширение.
  3. Создаем свое собственное ОписаниеОповещения, которое указывает на нашу процедуру в расширении.
  4. В нашей процедуре вызываем оригинальную процедуру обработки (если это необходимо) и затем свой код.

Пример реализации перехвата:


&Вместо("КомандаВыполнитьРассчитать")
Процедура Расш1_КомандаВыполнитьРассчитать(Команда)
    
    // Копируем логику подготовки (или вызываем ПродолжитьВызов, если структура позволяет)
    // Но меняем создание Оповещения:
    
    ПараметрыОповещения = Новый Структура("ОригинальноеОповещение", "ПослеЗавершенияРассчета");
    МоеОповещение = Новый ОписаниеОповещения("Расш1_МойОбработчикЗавершения", ЭтотОбъект, ПараметрыОповещения);
    
    // Запускаем операцию с нашим оповещением
    // ... (код запуска задания) ...
    
КонецПроцедуры

&НаКлиенте
Процедура Расш1_МойОбработчикЗавершения(Результат, ДополнительныеПараметры) Экспорт
    
    // Сначала вызываем стандартный обработчик (если нужно)
    // Для этого можно использовать программный вызов или заранее скопированную логику
    
    // Затем выполняем свои действия
    ВыполнитьДополнительныеДействияПослеФонового();
    
КонецПроцедуры

Решение 3: Использование обработчика ожидания

Если вмешательство в callback-функции кажется слишком сложным или структура кода основной конфигурации крайне запутана, можно использовать "ленивый" метод с обработчиком ожидания. Этот метод позволяет проверить состояние формы через небольшие промежутки времени.

Разберем, как это работает:

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

Пример кода:


&После("КомандаВыполнитьРассчитать")
Процедура Расш1_КомандаВыполнитьРассчитать_После(Команда)
    // Запускаем проверку через 0.1 секунды
    ПодключитьОбработчикОжидания("Расш1_ПроверитьЗавершениеФонового", 0.1, Истина);
КонецПроцедуры

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

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

Выясним причину выбора конкретного метода

Для выбора оптимального пути проанализируем структуру типового решения:

В большинстве современных конфигураций на базе БСП (ERP, УТ 11, ЗУП 3) используется стандартизированный метод ожидания. Ищите вызовы ДлительныеОперацииКлиент.ОжидатьЗавершения и расширяйте процедуру, передаваемую во втором параметре этого метода. Это обеспечит стабильную работу вашего кода после того, как все данные с сервера будут получены и отображены на форме пользователя.

← На главную