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