Как предотвратить ошибку серверных вызовов в глобальном обработчике ожидания при завершении работы 1С?

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

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

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

Начиная с версии платформы 8.3.8 при работе без режима совместимости внедрена асинхронная модель закрытия приложения. На уровне ядра 1С действует строгое правило: во время сценария завершения работы (включая цепочку событий модуля приложения ПередЗавершениемРаботыСистемы и ПриЗавершенииРаботыСистемы) любые серверные вызовы — как явные процедуры с директивами &НаСервере или &НаСервереБезКонтекста, так и неявные обращения к серверным данным — жестко блокируются. Поскольку платформа не снимает глобальные обработчики ожидания автоматически в момент нажатия пользователем кнопки закрытия, процедура таймера пытается выполнить сетевой запрос к серверу и вызывает исключение.

Способ 1. Использование глобального флага блокировки

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

Разберем по шагам, как реализовать этот подход в модуле управляемого приложения:

  1. Объявляем глобальную переменную на клиенте.
  2. В обработчике ожидания перед любым обращением к серверу проверяем значение переменной.
  3. В процедуре ПередНачаломЗавершенияРаботы или ПередЗавершениемРаботыСистемы устанавливаем флаг в значение Истина.

Посмотрим на пример программного кода в модуле управляемого приложения:


Перем СерверныеВызовыЗапрещены Экспорт;

Процедура ПриНачалеРаботыСистемы()
    СерверныеВызовыЗапрещены = Ложь;
    ПодключитьОбработчикОжидания("ПериодическаяПроверкаСервера", 60);
КонецПроцедуры

Процедура ПериодическаяПроверкаСервера() Экспорт
    Если СерверныеВызовыЗапрещены Тогда
        Возврат;
    КонецЕсли;

    ВыполнитьДействиеНаСервере();
КонецПроцедуры

Процедура ПередЗавершениемРаботыСистемы(Отказ, Предупреждения)
    СерверныеВызовыЗапрещены = Истина;
КонецПроцедуры

Обратите внимание: если завершение работы программы может быть отменено пользователем (например, при ответе «Нет» в интерактивном диалоге подтверждения выхода) или программно через параметр Отказ = Истина, флаг останется во взведенном состоянии, и обработчик больше не будет выполнять полезную работу в текущем сеансе. В таком случае необходимо предусмотреть сброс флага обратно в Ложь.

Способ 2. Явное отключение обработчика ожидания

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

Разберем реализацию этого метода в модуле управляемого приложения:


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

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

Способ 3. Перехват системного исключения

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

Посмотрим на пример безопасного вызова внутри обработчика:


Процедура ПериодическаяПроверкаСервера() Экспорт
    Попытка
        ВыполнитьДействиеНаСервере();
    Исключение
        // Подавляем системную ошибку запрета серверных вызовов при закрытии сеанса
    КонецПопытки;
КонецПроцедуры

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

Архитектурные рекомендации

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

  1. Перенос фоновой логики на сервер: если периодические действия не требуют прямого взаимодействия с интерфейсом пользователя, их следует переносить в регламентные задания.
  2. Использование Системы взаимодействия: если клиенту необходимо получать оперативные уведомления от сервера, предпочтительнее использовать механизмы push-уведомлений Системы взаимодействия платформы вместо постоянных таймеров.
  3. Контекст форм: если обработчик необходим для обновления конкретного интерфейсного элемента, подключайте его не глобально, а на уровне формы. В событиях формы ПередЗакрытием и ПриЗакрытии доступен стандартный параметр ЗавершениеРаботы, по которому можно точно отследить закрытие приложения, а сам таймер формы автоматически уничтожается вместе с ней.
← На главную