Оповещения в управляемых формах 1С 8.3: Оповестить, ОписаниеОповещения и асинхронные вызовы

· 9 мин

Содержание

Обработка оповещения в 1С — это два связанных механизма платформы 8.3. Первый — событие формы ОбработкаОповещения(ИмяСобытия, Параметр, Источник): его вызывает глобальный метод Оповестить(), и так одна открытая форма сообщает другим, что что-то изменилось. Второй — объект ОписаниеОповещения: в нём вы указываете, какую процедуру платформа вызовет, когда закончится немодальный диалог, закроется форма или завершится файловая операция.

Как выбирать: нужно разослать сигнал «данные поменялись» всем открытым окнам — Оповестить() и обработчик ОбработкаОповещения. Нужно дождаться ответа пользователя или результата операции и продолжить алгоритм — ОписаниеОповещения или, начиная с платформы 8.3.18, Асинх/Ждать. Разберём оба механизма с примерами кода, которые можно вставить в модуль формы.

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

Веб-клиент и мобильный клиент не умеют останавливать выполнение кода в ожидании ответа пользователя. Поэтому в 8.3 модальные окна (Вопрос(), ОткрытьФормуМодально(), Предупреждение()) заменили немодальными методами с суффиксом или префиксом «Показать»/«Начать».

Главная идея простая:

  • 🔹 вы вызываете метод, например ПоказатьВопрос();
  • 🔹 платформа показывает окно и сразу выполняет следующую строку кода;
  • 🔹 когда пользователь нажал кнопку, платформа вызывает процедуру из ОписаниеОповещения и передаёт туда результат.

Значит, код «после вопроса» нельзя писать следующей строкой. Его выносят в отдельную экспортную процедуру — обработчик оповещения.

Включение режима управляется свойством конфигурации Режим использования модальности (корень конфигурации в Конфигураторе, закладка свойств). Значения:

ЗначениеЧто происходит при вызове модального метода
ИспользоватьМодальные методы работают как раньше
Использовать с предупреждениямиРаботают, но при проверке конфигурации выводятся предупреждения
Не использоватьВызов модального метода вызывает ошибку во время выполнения

Типовые конфигурации (БП 3.0, ЗУП 3.1, УТ 11) работают в режиме «Не использовать». Рядом есть свойство Режим использования синхронных вызовов расширений платформы и внешних компонент — оно касается файловых операций и внешних компонент по тому же принципу.

Если дорабатываете типовую конфигурацию через расширение, пишите сразу асинхронно. Модальный вызов в расширении упадёт с ошибкой у всех пользователей, а не только в веб-клиенте.

Синтаксис: Оповестить, ОбработкаОповещения и ОписаниеОповещения

Метод Оповестить и событие формы

Отправка сигнала из любой клиентской процедуры:

Оповестить("ЗаписанЗаказ", Объект.Ссылка, ЭтотОбъект);

Параметры:

  1. ИмяСобытия — строка, по которой получатель понимает, что произошло.
  2. Параметр — любое значение: ссылка, структура, число.
  3. Источник — кто отправил; обычно форма-отправитель или ссылка.

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

&НаКлиенте
Процедура ОбработкаОповещения(ИмяСобытия, Параметр, Источник)

	Если ИмяСобытия <> "ЗаписанЗаказ" Тогда
		Возврат;
	КонецЕсли;

	Если Параметр = Объект.Заказ Тогда
		ОбновитьИтогиПоЗаказу();
	КонецЕсли;

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

Первой строкой всегда проверяйте ИмяСобытия. Обработчик срабатывает на любое оповещение в сеансе, в том числе от типовых механизмов.

ОповеститьОбИзменении — когда хватает его

ОповеститьОбИзменении(Ссылка) предназначен для обновления динамических списков, в которых виден изменённый объект. Если задача — перерисовать список после записи элемента из кода, используйте его. Для собственной логики в других формах нужен Оповестить().

Объект ОписаниеОповещения

Конструктор:

Оповещение = Новый ОписаниеОповещения(
	"ПослеОтветаНаВопрос",   // имя экспортной процедуры
	ЭтотОбъект,              // модуль, где она лежит
	ДопПараметры,            // любое значение, придёт вторым параметром
	"ПриОшибкеОбработки",    // необязательно: процедура обработки ошибки
	ЭтотОбъект);             // необязательно: модуль процедуры ошибки

Процедура-обработчик обязательно экспортная, иначе платформа её не найдёт:

&НаКлиенте
Процедура ПослеОтветаНаВопрос(Результат, ДополнительныеПараметры) Экспорт
	// Результат зависит от метода, который запускал оповещение
КонецПроцедуры

Модулем может быть форма (ЭтотОбъект) или клиентский общий модуль. Если обработчик в общем модуле, передавайте имя модуля без кавычек, например МойОбщийМодульКлиент.

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

Работа с формами: вопрос, выбор, закрытие

Вопрос пользователю

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

&НаКлиенте
Процедура ПроведениеПослеВопроса(Ответ, ДополнительныеПараметры) Экспорт
	Если Ответ <> КодВозвратаДиалога.Да Тогда
		Возврат;
	КонецЕсли;
	Записать(Новый Структура("РежимЗаписи", РежимЗаписиДокумента.Проведение));
КонецПроцедуры

Если окно закрылось по таймауту или крестиком, обработчик всё равно вызывается — с кодом КодВозвратаДиалога.Таймаут или Неопределено. Отсюда правило: проверяйте положительный ответ, а не отрицательный.

Открытие формы и возврат значения

Параметр ОписаниеОповещенияОЗакрытии в ОткрытьФорму() — седьмой по счёту:

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

&НаКлиенте
Процедура ПослеВыбораСклада(ВыбранныйСклад, ДополнительныеПараметры) Экспорт
	Если ЗначениеЗаполнено(ВыбранныйСклад) Тогда
		Объект.Склад = ВыбранныйСклад;
	КонецЕсли;
КонецПроцедуры

В открытой форме результат возвращают командой Закрыть(Значение). Значение и придёт первым параметром обработчика. Закрытие крестиком даст Неопределено.

Режим БлокироватьОкноВладельца визуально похож на модальность: пользователь не может работать в форме-владельце. Но код всё равно не ждёт закрытия — он идёт дальше.

Как передать контекст в обработчик

Переменные процедуры-инициатора в обработчик не попадают. Всё, что понадобится после ответа, кладите в третий параметр конструктора:

ДопПараметры = Новый Структура("ИндексСтроки, Количество", ТекущаяСтрока, 5);
Оповещение = Новый ОписаниеОповещения("ПослеВводаЧисла", ЭтотОбъект, ДопПараметры);
ПоказатьВводЧисла(Оповещение, 0, "Сколько штук?", 10, 0);

В ПослеВводаЧисла(Число, ДополнительныеПараметры) структура придёт вторым параметром.

Асинх и Ждать вместо цепочки обработчиков

С платформы 8.3.18 появились асинхронные функции, которые возвращают Обещание. Тот же вопрос пишется линейно:

&НаКлиенте
Асинх Процедура Провести(Команда)
	Ответ = Ждать ВопросАсинх("Провести документ?", РежимДиалогаВопрос.ДаНет);
	Если Ответ = КодВозвратаДиалога.Да Тогда
		Записать(Новый Структура("РежимЗаписи", РежимЗаписиДокумента.Проведение));
	КонецЕсли;
КонецПроцедуры
ПодходКогда выбиратьОграничения
ОписаниеОповещенияКонфигурация должна работать на платформе ниже 8.3.18; доработка типовой с таким стилемКод дробится на процедуры, контекст передаётся вручную
Асинх / ЖдатьРежим совместимости конфигурации не ниже 8.3.18Не у каждого старого метода есть вариант …Асинх
Оповестить()Сообщить другим открытым формам об измененииНе возвращает результат отправителю

Ждать работает только внутри процедуры или функции, помеченной Асинх. Режим совместимости смотрите в свойствах корня конфигурации: если там стоит версия ниже 8.3.18, ключевые слова не скомпилируются.

Асинхронность с фоновыми заданиями и HTTP-запросами

Фоновое задание выполняется на сервере и про клиента ничего не знает. Вызвать ОписаниеОповещения из фонового задания нельзя: оповещения живут только на клиенте. Связку строят так:

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

В конфигурациях на БСП (все типовые) эту работу делает подсистема «Длительные операции»:

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

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

Серверная часть ЗапуститьНаСервере() вызывает ДлительныеОперации.ВыполнитьВФоне() с именем экспортной процедуры общего модуля и возвращает её результат.

HTTP-запросы. HTTPСоединение работает на сервере синхронно: строка с Получить() или ОтправитьДляОбработки() ждёт ответа. Если сервис отвечает долго, у пользователя «висит» форма. Решения:

  • 🔹 задайте таймаут в конструкторе HTTPСоединение (шестой параметр, в секундах), чтобы запрос не ждал бесконечно;
  • 🔹 вынесите запрос в фоновое задание по схеме выше — клиент останется отзывчивым, а ответ придёт в обработчик оповещения.

Файловые операции на клиенте (НачатьПомещениеФайлаНаСервер, НачатьПодключениеРасширенияРаботыСФайлами) устроены как диалоги: принимают ОписаниеОповещения первым параметром или имеют вариант …Асинх.

Ошибки и отмена операций

Процедура обработки ошибки

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

&НаКлиенте
Процедура ПриОшибкеОбработки(ИнформацияОбОшибке, СтандартнаяОбработка, ДополнительныеПараметры) Экспорт
	СтандартнаяОбработка = Ложь;
	ПоказатьПредупреждение(, КраткоеПредставлениеОшибки(ИнформацияОбОшибке));
КонецПроцедуры

Если оставить СтандартнаяОбработка = Истина, платформа покажет своё стандартное окно ошибки. С Асинх/Ждать проще: обычная конструкция Попытка … Исключение … КонецПопытки вокруг Ждать перехватывает ошибку операции.

Отмена

Отмена в асинхронном коде — это не исключение, а особое значение результата. Шпаргалка:

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

Отменить уже запущенное фоновое задание можно серверным методом Отменить() у объекта ФоновоеЗадание. В БСП для этого есть ДлительныеОперации.ОтменитьВыполнениеЗадания(Идентификатор).

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

Производительность и практики, которые экономят время отладки

Держите ОбработкаОповещения лёгким. Он вызывается во всех открытых формах сеанса. Если у пользователя открыто 15 документов и каждый на любое оповещение ходит на сервер, одно Оповестить() превращается в 15 серверных вызовов.

  • ✅ выходите из процедуры сразу, если ИмяСобытия не ваше;
  • ✅ сверяйте Параметр с данными формы до серверного вызова;
  • ✅ игнорируйте собственное оповещение: Если Источник = ЭтотОбъект Тогда Возврат; КонецЕсли;.

Именуйте события по объекту и действию. «ЗаписанЗаказКлиента», «ИзмененыЦеныНоменклатуры». Короткое «Обновить» рано или поздно совпадёт с чужим оповещением из типовой конфигурации или другого расширения.

Передавайте в Параметр минимум. Ссылку или структуру из пары полей. Таблица на тысячу строк копируется в каждый вызов обработчика.

Не плодите цепочки из пяти обработчиков. Если после вопроса идёт ещё вопрос, потом выбор файла, потом запись — это сигнал переписать на Асинх/Ждать (если позволяет режим совместимости) или собрать общий контекст в одну структуру ДополнительныеПараметры и передавать её дальше.

Пишите свои процедуры по тому же контракту. Если ваша клиентская процедура внутри что-то спрашивает, пусть она принимает ОповещениеОЗавершении последним параметром и в конце вызывает ВыполнитьОбработкуОповещения(ОповещениеОЗавершении, Результат). Так её можно встроить в любую цепочку, и вызывающий код узнает о завершении.

Не храните ОписаниеОповещения в реквизите формы. Объект существует только на клиенте и не передаётся на сервер. При серверном вызове он не сериализуется — держите его в переменной модуля формы или в ДополнительныеПараметры.

Для отладки поставьте точку останова в обработчике и посмотрите стек вызовов: вызов придёт «из платформы», без процедуры-инициатора. Это нормальная картина для асинхронного кода, а не ошибка.

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

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