Оповещения в управляемых формах 1С 8.3: Оповестить, ОписаниеОповещения и асинхронные вызовы
Содержание
Обработка оповещения в 1С — это два связанных механизма платформы 8.3. Первый — событие формы ОбработкаОповещения(ИмяСобытия, Параметр, Источник): его вызывает глобальный метод Оповестить(), и так одна открытая форма сообщает другим, что что-то изменилось. Второй — объект ОписаниеОповещения: в нём вы указываете, какую процедуру платформа вызовет, когда закончится немодальный диалог, закроется форма или завершится файловая операция.
Как выбирать: нужно разослать сигнал «данные поменялись» всем открытым окнам — Оповестить() и обработчик ОбработкаОповещения. Нужно дождаться ответа пользователя или результата операции и продолжить алгоритм — ОписаниеОповещения или, начиная с платформы 8.3.18, Асинх/Ждать. Разберём оба механизма с примерами кода, которые можно вставить в модуль формы.
Как устроены асинхронные вызовы в платформе 1С 8.3
Веб-клиент и мобильный клиент не умеют останавливать выполнение кода в ожидании ответа пользователя. Поэтому в 8.3 модальные окна (Вопрос(), ОткрытьФормуМодально(), Предупреждение()) заменили немодальными методами с суффиксом или префиксом «Показать»/«Начать».
Главная идея простая:
- 🔹 вы вызываете метод, например
ПоказатьВопрос(); - 🔹 платформа показывает окно и сразу выполняет следующую строку кода;
- 🔹 когда пользователь нажал кнопку, платформа вызывает процедуру из
ОписаниеОповещенияи передаёт туда результат.
Значит, код «после вопроса» нельзя писать следующей строкой. Его выносят в отдельную экспортную процедуру — обработчик оповещения.
Включение режима управляется свойством конфигурации Режим использования модальности (корень конфигурации в Конфигураторе, закладка свойств). Значения:
| Значение | Что происходит при вызове модального метода |
|---|---|
| Использовать | Модальные методы работают как раньше |
| Использовать с предупреждениями | Работают, но при проверке конфигурации выводятся предупреждения |
| Не использовать | Вызов модального метода вызывает ошибку во время выполнения |
Типовые конфигурации (БП 3.0, ЗУП 3.1, УТ 11) работают в режиме «Не использовать». Рядом есть свойство Режим использования синхронных вызовов расширений платформы и внешних компонент — оно касается файловых операций и внешних компонент по тому же принципу.
Если дорабатываете типовую конфигурацию через расширение, пишите сразу асинхронно. Модальный вызов в расширении упадёт с ошибкой у всех пользователей, а не только в веб-клиенте.
Синтаксис: Оповестить, ОбработкаОповещения и ОписаниеОповещения
Метод Оповестить и событие формы
Отправка сигнала из любой клиентской процедуры:
Оповестить("ЗаписанЗаказ", Объект.Ссылка, ЭтотОбъект);
Параметры:
ИмяСобытия— строка, по которой получатель понимает, что произошло.Параметр— любое значение: ссылка, структура, число.Источник— кто отправил; обычно форма-отправитель или ссылка.
Платформа вызывает обработчик ОбработкаОповещения во всех открытых формах, у которых он есть. Чтобы обработчик появился, создайте его в палитре свойств формы, на закладке событий. Заготовка выглядит так:
&НаКлиенте
Процедура ОбработкаОповещения(ИмяСобытия, Параметр, Источник)
Если ИмяСобытия <> "ЗаписанЗаказ" Тогда
Возврат;
КонецЕсли;
Если Параметр = Объект.Заказ Тогда
ОбновитьИтогиПоЗаказу();
КонецЕсли;
КонецПроцедуры
Первой строкой всегда проверяйте ИмяСобытия. Обработчик срабатывает на любое оповещение в сеансе, в том числе от типовых механизмов.
ОповеститьОбИзменении — когда хватает его
ОповеститьОбИзменении(Ссылка) предназначен для обновления динамических списков, в которых виден изменённый объект. Если задача — перерисовать список после записи элемента из кода, используйте его. Для собственной логики в других формах нужен Оповестить().
Объект ОписаниеОповещения
Конструктор:
Оповещение = Новый ОписаниеОповещения(
"ПослеОтветаНаВопрос", // имя экспортной процедуры
ЭтотОбъект, // модуль, где она лежит
ДопПараметры, // любое значение, придёт вторым параметром
"ПриОшибкеОбработки", // необязательно: процедура обработки ошибки
ЭтотОбъект); // необязательно: модуль процедуры ошибки
Процедура-обработчик обязательно экспортная, иначе платформа её не найдёт:
&НаКлиенте
Процедура ПослеОтветаНаВопрос(Результат, ДополнительныеПараметры) Экспорт
// Результат зависит от метода, который запускал оповещение
КонецПроцедуры
Модулем может быть форма (ЭтотОбъект) или клиентский общий модуль. Если обработчик в общем модуле, передавайте имя модуля без кавычек, например МойОбщийМодульКлиент.
Чтобы вызвать оповещение вручную, есть метод ВыполнитьОбработкуОповещения(Оповещение, Результат). Он нужен, когда вы пишете свою процедуру, которая тоже работает асинхронно и принимает оповещение о завершении от вызывающего кода.
Работа с формами: вопрос, выбор, закрытие
Вопрос пользователю
&НаКлиенте
Процедура Провести(Команда)
Оповещение = Новый ОписаниеОповещения("ПроведениеПослеВопроса", ЭтотОбъект);
ПоказатьВопрос(Оповещение, "Провести документ?", РежимДиалогаВопрос.ДаНет);
КонецПроцедуры
&НаКлиенте
Процедура ПроведениеПослеВопроса(Ответ, ДополнительныеПараметры) Экспорт
Если Ответ <> КодВозвратаДиалога.Да Тогда
Возврат;
КонецЕсли;
Записать(Новый Структура("РежимЗаписи", РежимЗаписиДокумента.Проведение));
КонецПроцедуры
Если окно закрылось по таймауту или крестиком, обработчик всё равно вызывается — с кодом КодВозвратаДиалога.Таймаут или Неопределено. Отсюда правило: проверяйте положительный ответ, а не отрицательный.
Открытие формы и возврат значения
Параметр ОписаниеОповещенияОЗакрытии в ОткрытьФорму() — седьмой по счёту:
&НаКлиенте
Процедура ПодобратьСклад(Команда)
Оповещение = Новый ОписаниеОповещения("ПослеВыбораСклада", ЭтотОбъект);
ОткрытьФорму("Справочник.Склады.ФормаВыбора", , ЭтотОбъект, , , , Оповещение,
РежимОткрытияОкнаФормы.БлокироватьОкноВладельца);
КонецПроцедуры
&НаКлиенте
Процедура ПослеВыбораСклада(ВыбранныйСклад, ДополнительныеПараметры) Экспорт
Если ЗначениеЗаполнено(ВыбранныйСклад) Тогда
Объект.Склад = ВыбранныйСклад;
КонецЕсли;
КонецПроцедуры
В открытой форме результат возвращают командой Закрыть(Значение). Значение и придёт первым параметром обработчика. Закрытие крестиком даст Неопределено.
Режим БлокироватьОкноВладельца визуально похож на модальность: пользователь не может работать в форме-владельце. Но код всё равно не ждёт закрытия — он идёт дальше.
Как передать контекст в обработчик
Переменные процедуры-инициатора в обработчик не попадают. Всё, что понадобится после ответа, кладите в третий параметр конструктора:
ДопПараметры = Новый Структура("ИндексСтроки, Количество", ТекущаяСтрока, 5);
Оповещение = Новый ОписаниеОповещения("ПослеВводаЧисла", ЭтотОбъект, ДопПараметры);
ПоказатьВводЧисла(Оповещение, 0, "Сколько штук?", 10, 0);
В ПослеВводаЧисла(Число, ДополнительныеПараметры) структура придёт вторым параметром.
Асинх и Ждать вместо цепочки обработчиков
С платформы 8.3.18 появились асинхронные функции, которые возвращают Обещание. Тот же вопрос пишется линейно:
&НаКлиенте
Асинх Процедура Провести(Команда)
Ответ = Ждать ВопросАсинх("Провести документ?", РежимДиалогаВопрос.ДаНет);
Если Ответ = КодВозвратаДиалога.Да Тогда
Записать(Новый Структура("РежимЗаписи", РежимЗаписиДокумента.Проведение));
КонецЕсли;
КонецПроцедуры
| Подход | Когда выбирать | Ограничения |
|---|---|---|
ОписаниеОповещения | Конфигурация должна работать на платформе ниже 8.3.18; доработка типовой с таким стилем | Код дробится на процедуры, контекст передаётся вручную |
Асинх / Ждать | Режим совместимости конфигурации не ниже 8.3.18 | Не у каждого старого метода есть вариант …Асинх |
Оповестить() | Сообщить другим открытым формам об изменении | Не возвращает результат отправителю |
Ждать работает только внутри процедуры или функции, помеченной Асинх. Режим совместимости смотрите в свойствах корня конфигурации: если там стоит версия ниже 8.3.18, ключевые слова не скомпилируются.
Асинхронность с фоновыми заданиями и HTTP-запросами
Фоновое задание выполняется на сервере и про клиента ничего не знает. Вызвать ОписаниеОповещения из фонового задания нельзя: оповещения живут только на клиенте. Связку строят так:
- Клиент вызывает серверную процедуру формы.
- Сервер запускает задание через
ФоновыеЗадания.Выполнить()и возвращает его идентификатор. - Клиент периодически опрашивает состояние через
ПодключитьОбработчикОжидания(). - Когда задание завершилось, клиент забирает результат из временного хранилища и вызывает свой обработчик.
В конфигурациях на БСП (все типовые) эту работу делает подсистема «Длительные операции»:
&НаКлиенте
Процедура Сформировать(Команда)
Операция = ЗапуститьНаСервере();
Ожидание = ДлительныеОперацииКлиент.ПараметрыОжидания(ЭтотОбъект);
Ожидание.ВыводитьОкноОжидания = Истина;
Оповещение = Новый ОписаниеОповещения("ПослеФормирования", ЭтотОбъект);
ДлительныеОперацииКлиент.ОжидатьЗавершение(Операция, Оповещение, Ожидание);
КонецПроцедуры
&НаКлиенте
Процедура ПослеФормирования(Результат, ДополнительныеПараметры) Экспорт
Если Результат = Неопределено Тогда
Возврат; // пользователь отменил ожидание
КонецЕсли;
Если Результат.Статус = "Ошибка" Тогда
ПоказатьПредупреждение(, Результат.КраткоеПредставлениеОшибки);
Возврат;
КонецЕсли;
Данные = ПолучитьИзВременногоХранилища(Результат.АдресРезультата);
// разбор данных
КонецПроцедуры
Серверная часть ЗапуститьНаСервере() вызывает ДлительныеОперации.ВыполнитьВФоне() с именем экспортной процедуры общего модуля и возвращает её результат.
HTTP-запросы. HTTPСоединение работает на сервере синхронно: строка с Получить() или ОтправитьДляОбработки() ждёт ответа. Если сервис отвечает долго, у пользователя «висит» форма. Решения:
- 🔹 задайте таймаут в конструкторе
HTTPСоединение(шестой параметр, в секундах), чтобы запрос не ждал бесконечно; - 🔹 вынесите запрос в фоновое задание по схеме выше — клиент останется отзывчивым, а ответ придёт в обработчик оповещения.
Файловые операции на клиенте (НачатьПомещениеФайлаНаСервер, НачатьПодключениеРасширенияРаботыСФайлами) устроены как диалоги: принимают ОписаниеОповещения первым параметром или имеют вариант …Асинх.
Ошибки и отмена операций
Процедура обработки ошибки
Исключение внутри обработчика оповещения не возвращается к коду, который создал оповещение: тот давно завершился. Для перехвата есть четвёртый и пятый параметры конструктора ОписаниеОповещения. Процедура ошибки тоже экспортная:
&НаКлиенте
Процедура ПриОшибкеОбработки(ИнформацияОбОшибке, СтандартнаяОбработка, ДополнительныеПараметры) Экспорт
СтандартнаяОбработка = Ложь;
ПоказатьПредупреждение(, КраткоеПредставлениеОшибки(ИнформацияОбОшибке));
КонецПроцедуры
Если оставить СтандартнаяОбработка = Истина, платформа покажет своё стандартное окно ошибки. С Асинх/Ждать проще: обычная конструкция Попытка … Исключение … КонецПопытки вокруг Ждать перехватывает ошибку операции.
Отмена
Отмена в асинхронном коде — это не исключение, а особое значение результата. Шпаргалка:
| Метод | Что придёт при отмене |
|---|---|
ПоказатьВопрос | КодВозвратаДиалога.Отмена, .Таймаут или Неопределено |
ОткрытьФорму с оповещением о закрытии | Неопределено (закрыли без Закрыть(Значение)) |
ПоказатьВводЧисла, ПоказатьВводСтроки | Неопределено |
ДлительныеОперацииКлиент.ОжидатьЗавершение | Неопределено, если пользователь закрыл окно ожидания |
Отменить уже запущенное фоновое задание можно серверным методом Отменить() у объекта ФоновоеЗадание. В БСП для этого есть ДлительныеОперации.ОтменитьВыполнениеЗадания(Идентификатор).
Производительность и практики, которые экономят время отладки
Держите ОбработкаОповещения лёгким. Он вызывается во всех открытых формах сеанса. Если у пользователя открыто 15 документов и каждый на любое оповещение ходит на сервер, одно Оповестить() превращается в 15 серверных вызовов.
- ✅ выходите из процедуры сразу, если
ИмяСобытияне ваше; - ✅ сверяйте
Параметрс данными формы до серверного вызова; - ✅ игнорируйте собственное оповещение:
Если Источник = ЭтотОбъект Тогда Возврат; КонецЕсли;.
Именуйте события по объекту и действию. «ЗаписанЗаказКлиента», «ИзмененыЦеныНоменклатуры». Короткое «Обновить» рано или поздно совпадёт с чужим оповещением из типовой конфигурации или другого расширения.
Передавайте в Параметр минимум. Ссылку или структуру из пары полей. Таблица на тысячу строк копируется в каждый вызов обработчика.
Не плодите цепочки из пяти обработчиков. Если после вопроса идёт ещё вопрос, потом выбор файла, потом запись — это сигнал переписать на Асинх/Ждать (если позволяет режим совместимости) или собрать общий контекст в одну структуру ДополнительныеПараметры и передавать её дальше.
Пишите свои процедуры по тому же контракту. Если ваша клиентская процедура внутри что-то спрашивает, пусть она принимает ОповещениеОЗавершении последним параметром и в конце вызывает ВыполнитьОбработкуОповещения(ОповещениеОЗавершении, Результат). Так её можно встроить в любую цепочку, и вызывающий код узнает о завершении.
Не храните ОписаниеОповещения в реквизите формы. Объект существует только на клиенте и не передаётся на сервер. При серверном вызове он не сериализуется — держите его в переменной модуля формы или в ДополнительныеПараметры.
Для отладки поставьте точку останова в обработчике и посмотрите стек вызовов: вызов придёт «из платформы», без процедуры-инициатора. Это нормальная картина для асинхронного кода, а не ошибка.