Как подключить обработчик ожидания в 1С 8.3

· 8 мин

Содержание

Обработчик ожидания подключают методом ПодключитьОбработчикОжидания("ИмяПроцедуры", Интервал) прямо в модуле формы. Процедура-обработчик — клиентская, без параметров, с директивой &НаКлиенте; интервал задаётся в секундах. Этого достаточно, чтобы 1С сама вызывала вашу процедуру через заданные промежутки времени — для автообновления списка, опроса фонового задания или отложенного действия после открытия формы.

Дальше — синтаксис метода, куда его ставить, как через него следить за серверным заданием и как не оставить в базе «вечный» таймер, который дёргает сервер каждую секунду. Код — под платформу 8.3 с управляемыми формами (интерфейс «Такси»).

Что это за механизм и где он срабатывает

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

Ключевой момент: обработчик работает только в клиентском контексте — на толстом и тонком клиенте, в веб-клиенте. На сервере его нет. Если вам нужна параллельная работа именно на сервере, это другой инструмент — фоновые и регламентные задания (о связке с ними ниже).

Из этого следуют два практических вывода:

  • 🧵 Код обработчика выполняется в основном потоке клиента. Тяжёлая операция внутри него подвесит интерфейс — пользователь не сможет кликать, пока процедура не отработает.
  • ⏱ Интервал — это минимальная задержка, а не точное время. Если клиент занят (открыто блокирующее окно, выполняется длинный серверный вызов), вызов откладывается.

Технически платформа ставит вызов вашей процедуры в очередь и исполняет её, как только освобождается основной поток клиента. Поэтому таймер не прерывает выполняющийся код — он дожидается паузы.

Типовые задачи, под которые механизм подходит:

  • 🔄 периодически обновлять данные на форме (список заявок, остатки, статусы);
  • 📡 опрашивать состояние фонового задания и показать результат по завершении;
  • ⏳ выполнить отложенное действие один раз после открытия формы (подсказку, автофокус);
  • 💾 автосохранять черновик или настройки с заданной частотой.

Для лёгких повторяющихся действий обработчик ожидания — правильный выбор. Для расчётов и импорта выносите работу на сервер, а таймер используйте только как «пульс» для опроса.

Синтаксис метода и его параметры

Полная форма вызова:

ПодключитьОбработчикОжидания(<ИмяПроцедуры>, <Интервал>, <Однократно>)

Параметры:

  1. ИмяПроцедуры (обязательный, тип Строка) — имя процедуры, которую надо вызывать. Передаётся строкой, без скобок и параметров. Строка должна точно совпадать с именем процедуры в модуле.
  2. Интервал (обязательный, тип Число) — промежуток в секундах. Значение больше нуля; максимум — 2 073 600 секунд (это ровно 24 суток). Если нужен интервал меньше секунды, он должен быть кратен 0,1 (то есть минимум 0,1 секунды).
  3. Однократно (необязательный, тип Булево, по умолчанию Ложь) — если Истина, процедура вызывается один раз, после чего обработчик снимается сам.

Вызванная процедура обязана быть без параметров и доступна на клиенте.

В управляемом приложении используют метод формы ПодключитьОбработчикОжидания. Внутри модуля формы его вызывают просто по имени — без ЭтотОбъект. Одноимённый метод глобального контекста считается устаревшим, новый код на него опирать не стоит.

Повторный вызов ПодключитьОбработчикОжидания с тем же именем процедуры не создаёт второй таймер, а переустанавливает интервал для уже подключённого. А вот процедуры с разными именами работают параллельно — можно держать несколько обработчиков сразу.

Подключаем обработчик на клиенте

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

&НаКлиенте
Процедура ПриОткрытии(Отказ)
    ПодключитьОбработчикОжидания("ОбновитьДанныеНаФорме", 5);
КонецПроцедуры

&НаКлиенте
Процедура ОбновитьДанныеНаФорме()
    ОбновитьДанныеНаСервере();
КонецПроцедуры

&НаСервере
Процедура ОбновитьДанныеНаСервере()
    // перечитываем данные, пересчитываем реквизиты формы
КонецПроцедуры

Разберём по шагам, что здесь происходит:

  1. ПриОткрытии — клиентское событие формы. Как только форма открылась, регистрируем таймер.
  2. Имя "ОбновитьДанныеНаФорме" в строке точно совпадает с именем клиентской процедуры ниже.
  3. Сама процедура-обработчик только запускает работу, а тяжёлую часть делегирует серверному методу.

Если действие нужно выполнить один раз с задержкой — например, показать подсказку через 2 секунды после открытия, — добавляем третий параметр:

ПодключитьОбработчикОжидания("ПоказатьПодсказку", 2, Истина);

С флагом Истина обработчик сработает один раз и отключится сам — отдельный ОтключитьОбработчикОжидания не нужен.

Проверьте перед подключением обработчика
Выполнено: 0 / 5

Опрос фонового задания через обработчик

Самый частый сценарий — долгая операция на сервере (импорт, выгрузка, пересчёт), а на клиенте надо показать результат, когда она закончится. Обработчик ожидания здесь играет роль «пульса»: он периодически спрашивает у сервера, завершилось ли задание.

Схема такая: клиент запускает фоновое задание на сервере, сохраняет его идентификатор, подключает обработчик и начинает опрос.

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

&НаКлиенте
Процедура ЗапуститьИмпорт(Команда)
    ЗапуститьЗаданиеНаСервере();
    ПодключитьОбработчикОжидания("ПроверитьСостояниеЗадания", 1);
КонецПроцедуры

&НаКлиенте
Процедура ПроверитьСостояниеЗадания()
    Если ЗаданиеЗавершено(ИдентификаторЗадания) Тогда
        ОтключитьОбработчикОжидания("ПроверитьСостояниеЗадания");
        ОбработатьРезультатЗадания();
    КонецЕсли;
КонецПроцедуры

&НаСервереБезКонтекста
Функция ЗаданиеЗавершено(Ид)
    Задание = ФоновыеЗадания.НайтиПоУникальномуИдентификатору(
        Новый УникальныйИдентификатор(Ид));
    Возврат Задание = Неопределено
        ИЛИ Задание.Состояние <> СостояниеФоновогоЗадания.Активно;
КонецФункции

Что важно в этом коде:

  • Идентификатор задания хранится в реквизите формы (ИдентификаторЗадания) как строка — объект ФоновоеЗадание на клиент не передаётся, с ним работают только на сервере.
  • Как только состояние задания перестаёт быть Активно, опрос прекращаем вызовом ОтключитьОбработчикОжидания. Иначе таймер продолжит слать серверные вызовы вхолостую.
  • Состояние может быть не только Завершено, но и ЗавершеноАварийно или ОтмененоАдминистратором — поэтому проверяем «не активно», а дальше разбираемся с результатом.

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

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

Отключение обработчика и обработка ошибок

Отключение — это половина правильной работы с таймером:

ОтключитьОбработчикОжидания("ПроверитьСостояниеЗадания");

Вызывайте ОтключитьОбработчикОжидания в трёх ситуациях:

  • 🎯 задача, ради которой подключался таймер, выполнена (задание завершилось);
  • 🚪 обработчик привязан к форме, а форма закрывается;
  • 🔁 нужно сменить интервал — тогда достаточно повторного ПодключитьОбработчикОжидания, отдельное отключение не обязательно.

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

&НаКлиенте
Процедура ПередЗакрытием(Отказ, ЗавершениеРаботы, ТекстПредупреждения, СтандартнаяОбработка)
    ОтключитьОбработчикОжидания("ОбновитьДанныеНаФорме");
КонецПроцедуры

Вызывать ОтключитьОбработчикОжидания для процедуры, которая и так не подключена, безопасно — ошибки не будет. Так что перестраховаться дешевле, чем оставить висящий таймер.

Теперь про ошибки. Если внутри процедуры-обработчика возникнет исключение, оно всплывёт на клиент как обычная ошибка — и пользователь увидит её при каждом срабатывании, пока таймер подключён. Поэтому рискованный код оборачивают в Попытка:

&НаКлиенте
Процедура ПроверитьСостояниеЗадания()
    Попытка
        Если ЗаданиеЗавершено(ИдентификаторЗадания) Тогда
            ОтключитьОбработчикОжидания("ПроверитьСостояниеЗадания");
            ОбработатьРезультатЗадания();
        КонецЕсли;
    Исключение
        ОтключитьОбработчикОжидания("ПроверитьСостояниеЗадания");
        ПоказатьПредупреждение("Не удалось проверить задание: "
            + ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
    КонецПопытки;
КонецПроцедуры

Две частые причины ошибок при срабатывании:

  1. Имя процедуры не найдено. Строка в ПодключитьОбработчикОжидания не совпадает с именем процедуры (опечатка, другой регистр не критичен, но лишний пробел — да), либо процедура объявлена без &НаКлиенте.
  2. У процедуры есть параметры. Обработчик вызывает процедуру без аргументов — если в объявлении есть параметры, будет ошибка. Уберите их или заведите промежуточную процедуру-обёртку.

Рабочие приёмы и как не нагрузить клиент

Несколько правил, которые отличают аккуратный код от «таймера, который тормозит базу»:

ПриёмЗачем
Интервал не меньше реально нужногоОпрос каждую секунду — это серверный вызов каждую секунду на каждого пользователя
Адаптивный интервал (1 → 2 → 5 сек)Долгое задание не нужно опрашивать так же часто, как только что запущенное
Тяжёлый код — на серверТело обработчика выполняется в основном потоке; длинная операция подвесит интерфейс
Однократно = Истина для разовых действийНе придётся вручную отключать таймер после одного срабатывания
Отключать по завершении«Забытый» обработчик продолжает слать вызовы, пока открыта форма

Пример адаптивного опроса — наращиваем интервал, чтобы не долбить сервер:

&НаКлиенте
Процедура ПроверитьСостояниеЗадания()
    Если ЗаданиеЗавершено(ИдентификаторЗадания) Тогда
        ОтключитьОбработчикОжидания("ПроверитьСостояниеЗадания");
        ОбработатьРезультатЗадания();
    Иначе
        Если ТекущийИнтервал < 10 Тогда
            ТекущийИнтервал = ТекущийИнтервал + 2;
            ПодключитьОбработчикОжидания("ПроверитьСостояниеЗадания", ТекущийИнтервал);
        КонецЕсли;
    КонецЕсли;
КонецПроцедуры

Здесь повторный ПодключитьОбработчикОжидания переустанавливает тот же обработчик с бо́льшим интервалом — новый таймер не плодится. Счётчик ТекущийИнтервал — это реквизит формы; перед запуском задания его задают начальным значением (например, 1).

Чтобы пользователь во время опроса видел, что процесс идёт, а не завис, показывайте неблокирующее оповещение методом ПоказатьОповещениеПользователя. Оно не прерывает работу и не требует клика — в отличие от Предупреждение, которое заблокировало бы клиент и вместе с ним сам обработчик.

И последнее про предсказуемость. Обработчик ожидания не гарантирует срабатывание точно по расписанию: он вызывается в простое клиента. Если пользователь открыл модальное окно или выполняется длинный синхронный вызов — очередной вызов сдвинется. Для действительно точного по времени поведения обработчик ожидания не предназначен; его сила в простоте и лёгком фоновом опросе.

Чем обработчик ожидания отличается от фонового задания?
Обработчик ожидания — клиентский таймер, его код выполняется в основном потоке клиента. Фоновое задание — отдельный серверный поток, работает параллельно и не зависит от клиента. На практике их связывают: задание считает на сервере, обработчик на клиенте опрашивает его состояние.
Можно ли подключить сразу несколько обработчиков?
Да. Обработчики с разными именами процедур работают одновременно и независимо. А вот повторный вызов с тем же именем не добавляет второй таймер, а переустанавливает интервал существующего.
Почему процедура вызывается не точно через заданный интервал?
Обработчик срабатывает в момент простоя клиента. Если интерфейс занят, открыто блокирующее окно или идёт длинный серверный вызов, вызов откладывается. Интервал — это минимальная задержка, а не точное время.
Работает ли обработчик ожидания на сервере?
Нет. Это чисто клиентский механизм — толстый клиент, тонкий клиент, веб-клиент. Серверной периодической работой занимаются фоновые и регламентные задания.
Какой минимальный интервал можно задать?
Значение меньше 1 секунды должно быть кратно 0,1 — то есть минимум 0,1 секунды. Максимум — 2 073 600 секунд (24 суток).