При переходе с обычных форм на управляемые многие разработчики сталкиваются с неожиданным поведением системы: вызывая диалоговое окно с вопросом, программа не останавливается на этой строке, а продолжает выполнять следующий за ней код. В итоге алгоритм отрабатывает некорректно, так как ответ пользователя еще не получен. Рассмотрим подробнее, почему это происходит и как правильно адаптировать свой код под требования современной платформы 1С:Предприятие 8.3.
В старых версиях платформы и в обычных формах использовался метод Вопрос(). Это был модальный вызов: выполнение кода полностью замирало на строке с вызовом функции, пока пользователь не нажмет кнопку в диалоговом окне. Программа получала результат сразу в ту же переменную.
В управляемом интерфейсе, особенно при работе в веб-клиенте или на мобильных устройствах, использование модальных окон запрещено или ограничено по техническим причинам архитектуры браузеров. Вместо них используются немодальные (асинхронные) вызовы, такие как ПоказатьВопрос(). Проанализируем механизм их работы: когда система встречает ПоказатьВопрос(), она лишь «дает команду» браузеру отрисовать окно и немедленно переходит к следующей строке кода. Ответ пользователя придет позже, в совершенно другую процедуру.
Самый распространенный способ решения задачи — это разделение одной большой процедуры на две части. Рассмотрим, как это сделать правильно. Весь код, который должен выполниться после того, как пользователь нажмет «Да» или «Нет», нужно вынести в отдельную экспортную процедуру.
Выясним, как выглядел проблемный код автора:
// В одной процедуре
ПоказатьВопрос(Новый ОписаниеОповещения("ОбработкаКомандыЗавершение2", ЭтаФорма, ТЧ), "Удалить строки?", РежимДиалогаВопрос.ДаНет);
// Весь остальной код, который выполнялся СРАЗУ, не дожидаясь ответа
Правильный подход заключается в том, чтобы оставить в первой процедуре только вызов вопроса, а всю логику перенести во вторую. Разберем по шагам структуру обработчика:
&НаКлиенте
Процедура КомандаУдалить(Команда)
// Подготавливаем дополнительные параметры, если они нужны (например, имя таблицы)
ДополнительныеПараметры = Новый Структура;
ДополнительныеПараметры.Вставить("ИмяТаблицы", "Товары");
Оповещение = Новый ОписаниеОповещения("ОбработкаКомандыЗавершение2", ЭтаФорма, ДополнительныеПараметры);
ТекстВопроса = "Удалить текущие строки таблицы?";
ПоказатьВопрос(Оповещение, ТекстВопроса, РежимДиалогаВопрос.ДаНет);
// ВАЖНО: После этой строки кода быть не должно,
// либо он не должен зависеть от ответа пользователя.
КонецПроцедуры
&НаКлиенте
Процедура ОбработкаКомандыЗавершение2(Результат, ДополнительныеПараметры) Экспорт
// Система передает в "Результат" выбор пользователя
Если Результат = КодВозвратаДиалога.Да Тогда
// Здесь выполняем действия по удалению
ИмяТЧ = ДополнительныеПараметры.ИмяТаблицы;
ЭтаФорма[ИмяТЧ].Очистить();
КонецЕсли;
КонецПроцедуры
Если ваша конфигурация работает на версии платформы 8.3.18 и выше, и в свойствах конфигурации выключена модальность, мы можем использовать более элегантный способ. Он позволяет писать асинхронный код так, будто он синхронный, используя ключевые слова Асинх и Ждать.
Рассмотрим пример, который визуально сохраняет логическую последовательность кода в одной процедуре:
&НаКлиенте
Асинх Процедура КомандаУдалитьАсинх(Команда)
ТекстВопроса = "Вы уверены, что хотите очистить таблицу?";
// Код замрет на этой строке до ответа пользователя,
// но интерфейс при этом не зависнет!
Ответ = Ждать ВопросАсинх(ТекстВопроса, РежимДиалогаВопрос.ДаНет);
Если Ответ = КодВозвратаДиалога.Да Тогда
Объект.Товары.Очистить();
Сообщить("Таблица очищена");
Иначе
Возврат;
КонецЕсли;
// Продолжение логики в той же процедуре
РассчитатьИтогиНаФорме();
КонецПроцедуры
Использование ВопросАсинх значительно упрощает жизнь разработчику, избавляя от необходимости плодить десятки процедур-оповещений и передавать контекст через ДополнительныеПараметры.
Проанализируем ситуацию, упомянутую в обсуждении: использование немодальных окон (через ПоказатьВопрос или ВопросАсинх) таит в себе опасность. Пока диалоговое окно висит на экране, основная форма не блокируется автоматически (если не приняты специальные меры).
Это означает, что пользователь может:
Чтобы избежать этих проблем, мы рекомендуем либо блокировать элементы интерфейса перед вызовом вопроса, либо использовать вспомогательные общие формы из БСП (Библиотеки стандартных подсистем), которые умеют блокировать окно владельца. Если же вы создаете свою форму вопроса, открывайте её методом ОткрытьФорму, устанавливая режим открытия окна в значение БлокироватьОкноВладельца.
Часто программисты забывают, что локальные переменные первой процедуры недоступны во второй. Если вам нужно передать данные (например, структуру с какими-то флагами или массив ссылок), всегда используйте третий параметр конструктора Новый ОписаниеОповещения(ИмяПроцедуры, Модуль, Параметры). Эти параметры придут в процедуру-обработчик в неизменном виде, что позволит вам продолжить работу с теми же данными.
Посмотрим на пример передачи сложного контекста:
ПараметрыДействия = Новый Структура;
ПараметрыДействия.Вставить("ТекущийДокумент", ТекущийОбъект.Ссылка);
ПараметрыДействия.Вставить("НужноПересчитать", Истина);
Оповещение = Новый ОписаниеОповещения("ЗавершитьДействие", ЭтотОбъект, ПараметрыДействия);
ПоказатьВопрос(Оповещение, "Выполнить операцию?", РежимДиалогаВопрос.ДаНет);
Переход на асинхронную модель — это не прихоть платформы, а требование времени для обеспечения кроссплатформенности. Главное правило: если вы используете ПоказатьВопрос, забудьте о выполнении кода ниже этой функции. Весь «хвост» алгоритма должен переместиться в процедуру завершения. А если платформа позволяет — смело используйте Ждать ВопросАсинх, это сделает ваш код чище и понятнее.