Как приостановить выполнение кода для ожидания ответа пользователя в 1С?

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

Многие разработчики 1С при переходе на новые версии платформы сталкиваются с тем, что стандартные диалоговые окна перестают блокировать выполнение кода. Платформа просто идет дальше, не дожидаясь, пока пользователь нажмет кнопку «Да» или «Нет». Давайте разберем по шагам, почему так происходит, и посмотрим, как правильно организовать ожидание ответа пользователя в современных конфигурациях, в том числе при работе через расширения.

Почему метод ПоказатьВопрос не останавливает код?

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

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

Решение 1: Современный подход Async/Await (для платформы 8.3.18 и выше)

Рассмотрим самый современный подход, который делает код визуально «линейным», как это было раньше с модальными окнами. Для этого используем метод ВопросАсинх в связке с оператором Ждать. Похожий подход с применением Асинх методов используется и для работы с файлами.

Посмотрим на пример реализации:


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

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

Важно: этот метод будет работать, только если в свойствах конфигурации «Режим использования модальности» установлен в значение «Не использовать», а также разрешена асинхронность.

Решение 2: Классический подход через ОписаниеОповещения

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

Разберем пример создания вопроса с оповещением:


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

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

Решение 3: Как задать вопрос перед записью документа (Сложный сценарий)

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

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


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

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

&НаКлиенте
Процедура ПродолжениеПослеВопроса(Результат, ПараметрыЗаписи) Экспорт
    
    Если Результат = КодВозвратаДиалога.Нет Тогда
        Возврат;
    КонецЕсли;
    
    // Устанавливаем флаг, что вопрос задан и ответ получен
    ПараметрыЗаписи.Вставить("ПоказанВопросПользователю", Истина);
    
    // Инициируем запись заново. В этот раз условие в ПередЗаписью будет пройдено
    Записать(ПараметрыЗаписи);
    
КонецПроцедуры

Дополнительные нюансы, о которых нужно знать

Рассмотрим несколько важных моментов при использовании немодальных вопросов:

  1. Передача ответа на сервер: Флаг из ПараметрыЗаписи доступен только в клиентских обработчиках. Если вам нужно передать решение пользователя в модуль объекта на сервере, запишите значение в ДополнительныеСвойства объекта перед вызовом программной записи.
  2. Опасность двойных проверок: При использовании схемы с прерыванием и повторным вызовом Записать() помните, что все проверки заполнения могут сработать дважды. Оптимизируйте код так, чтобы тяжелые алгоритмы не выполнялись вхолостую.
  3. Использование расширений: При использовании аннотации &Перед, ваш код выполняется до основной логики. Установка Отказ = Истина предотвратит типовую запись, но некоторые формы могут иметь собственные перехватчики. Иногда требуется использовать &Вместо, вызывая ПродолжитьВызов() только при успешном прохождении всех условий.
  4. Мобильный клиент: Если конфигурация предназначена для работы на мобильных устройствах, использование любых блокирующих (модальных) методов строго запрещено платформой. Применяйте только ПоказатьВопрос или ВопросАсинх.
← На главную