При разработке интерактивных интерфейсов на платформе 1С:Предприятие часто возникает задача регулярного выполнения фоновых действий. Это может быть обновление статусов документов, проверка новых сообщений или пересчет показателей на форме. Стандартным механизмом для таких задач является метод ПодключитьОбработчикОжидания.
Однако многие разработчики сталкиваются с техническим ограничением этого метода: он умеет запускать только процедуру, не принимает параметров и не возвращает значений. В этой статье мы подробно разберем, как обойти эти ограничения, правильно передать данные в периодический процесс и получить результат вычислений, не нарушая архитектуру управляемого приложения.
Давайте проанализируем, как работает метод ПодключитьОбработчикОжидания. Его синтаксис предполагает передачу имени процедуры в виде строки.
Основные ограничения, с которыми мы сталкиваемся:
Автор вопроса столкнулся с необходимостью не просто выполнить действие, а передать входные данные в функцию и получить ответ. Давайте разберем пошаговое решение этой задачи.
Поскольку мы не можем изменить поведение платформы, нам необходимо подстроиться под него. Самым эффективным решением, предложенным в обсуждении, является использование паттерна "Обертка" (Wrapper).
Идея проста: мы создаем процедуру без параметров, которая удовлетворяет требованиям метода ПодключитьОбработчикОжидания. Внутри этой процедуры мы вызываем нашу целевую функцию уже с нужными параметрами.
Рассмотрим структуру кода:
&НаКлиенте
Процедура ПриОткрытии()
// Запускаем обработчик, который будет срабатывать каждые 10 секунд
ПодключитьОбработчикОжидания("ОбработчикПериодическойЗадачи", 10);
КонецПроцедуры
&НаКлиенте
Процедура ОбработчикПериодическойЗадачи()
// Это и есть наша "обертка".
// Здесь мы можем подготовить данные и вызвать нужную функцию
// 1. Получаем параметры (об этом ниже)
// 2. Вызываем функцию
ВыполнитьСложныйРасчет();
КонецПроцедуры
Самый острый вопрос в теме форума: "Откуда взять параметры, если в процедуру их передать нельзя?".
В управляемых формах 1С нет "глобальной памяти" в привычном понимании процедурного программирования, которая была бы доступна везде и всегда без нюансов контекста. Однако у нас есть контекст самой формы. Рассмотрим варианты хранения данных, предложенные участниками обсуждения.
Это наиболее надежный способ. Реквизиты формы доступны как на Клиенте, так и на Сервере (при контекстном вызове). Вы создаете реквизит формы (например, ПараметрыРасчета), заполняете его перед запуском обработчика или изменяете в процессе работы пользователя.
Преимущества:
&НаСервере).Реализация:
&НаКлиенте
Процедура ОбработчикПериодическойЗадачи()
// Параметр берется из реквизита формы Объект или созданного нами реквизита
ТекущийПараметр = Объект.Склад;
// Передаем значение в функцию
Результат = ФункцияРасчета(ТекущийПараметр);
КонецПроцедуры
Автор темы спрашивал про использование Перем в шапке модуля. Этот вариант возможен, но имеет существенные недостатки в клиент-серверной архитектуре.
Переменная, объявленная в модуле формы, существует только в том контексте, где она инициализирована. В управляемом приложении переменная модуля на Клиенте будет сохранять значение между вызовами ОбработчикПериодическойЗадачи. Однако, если ваша функция требует обращения к Серверу, эти переменные "невидимы" для сервера, их придется передавать явным параметром.
Функции в программировании возвращают значение. Но ПодключитьОбработчикОжидания ничего не ждет в ответ. Куда девать результат?
Здесь мы используем принцип "Побочных эффектов" (Side Effects). Результат выполнения функции должен быть записан во "внешнюю среду" — в элементы формы или реквизиты.
Давайте посмотрим на полный пример реализации, решающий проблему автора.
// Предположим, у нас есть реквизиты формы:
// "ПараметрДляРасчета" (Число) - входные данные
// "РезультатРасчета" (Число) - сюда запишем ответ
&НаКлиенте
Процедура ПриОткрытии()
// Запускаем цикл
ПодключитьОбработчикОжидания("ВыполнитьПериодическуюОперацию", 5);
КонецПроцедуры
&НаКлиенте
Процедура ВыполнитьПериодическуюОперацию()
// 1. Считываем текущее состояние (ПАРАМЕТРЫ)
// Мы берем их из реквизитов формы, которые пользователь мог изменить
ВходноеЗначение = ЭтотОбъект.ПараметрДляРасчета;
// 2. Вызываем функцию обработки
НовоеЗначение = ПолучитьДанныеССервера(ВходноеЗначение);
// 3. Сохраняем результат (РЕЗУЛЬТАТ)
// Записываем данные обратно в форму, чтобы пользователь их увидел
ЭтотОбъект.РезультатРасчета = НовоеЗначение;
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПолучитьДанныеССервера(Знач ВходнойПараметр)
// Выполняем логику
Возврат ВходнойПараметр * 2;
КонецФункции
Иногда задача сложнее: нужно при каждом срабатывании таймера обрабатывать разные данные (например, перебирать массив товаров по одному, чтобы не вешать интерфейс).
В этом случае в реквизите формы можно хранить список (Массив или СписокЗначений). А процедура-обертка будет работать как обработчик очереди.
Давайте разберем алгоритм такого решения:
ОчередьЗадач (тип Произвольный, при инициализации помещаем туда Массив).
&НаКлиенте
Процедура ОбработатьОчередь()
// Проверяем наличие задач
Если ОчередьЗадач.Количество() = 0 Тогда
ОтключитьОбработчикОжидания("ОбработатьОчередь");
Возврат;
КонецЕсли;
// Извлекаем параметр для текущего такта
ТекущийТовар = ОчередьЗадач[0];
ОчередьЗадач.Удалить(0);
// Выполняем функцию с параметром
ОбработатьТовар(ТекущийТовар);
КонецПроцедуры
Если параметры представляют собой большие объемы данных (таблицы значений, файлы, структуры), хранить их в реквизитах формы нерационально, так как это утяжеляет контекст и замедляет работу формы при каждом клике.
В обсуждении справедливо упомянули ПоместитьВоВременноеХранилище. В этом случае:
ПолучитьИзВременногоХранилища, обрабатывает их и, при необходимости, помещает результат обратно.Подводя итог обсуждения проблемы, можно выделить ключевые моменты для правильной реализации:
ПодключитьОбработчикОжидания — это невозможно архитектурно.