Как прервать длительную загрузку в цикле без модального окна в 1С?

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

При разработке обработок загрузки больших массивов данных (например, объемных прайс-листов из Excel) часто возникает необходимость дать пользователю возможность прервать процесс. В старых версиях платформы 1С на обычных формах это решалось простым модальным вызовом Вопрос() прямо внутри цикла загрузки. Однако в парадигме управляемых форм (УФ) и интерфейса "Такси" использование модальных окон запрещено или настоятельно не рекомендуется.

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

Способ 1: Современный подход с использованием Async/Await (начиная с платформы 8.3.18)

Если конфигурация работает на платформе 1С:Предприятие 8.3.18 и выше, мы можем использовать современные механизмы асинхронного программирования. Посмотрим на новые модификаторы Асинх и оператор Ждать, которые позволяют приостановить выполнение процедуры (не вешая интерфейс) до получения ответа от пользователя.

Рассмотрим пример реализации такого цикла загрузки:


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

В этом подходе нам не нужно разрывать цикл или писать сложные обработчики оповещений с передачей контекста. Метод ВопросАсинх() вместе с Ждать элегантно решает проблему.

Способ 2: Архитектурно-правильный подход через Фоновые задания (БСП)

Проанализируем ситуацию с точки зрения производительности. Перебирать огромный Excel-файл на клиенте и построчно дергать сервер для записи данных — это крайне неэффективно. Правильная логика приложения (особенно в тонком и веб-клиенте) задумывается иначе.

Вместо того чтобы задавать вопросы в цикле, перенесем выполнение задачи на сервер в фоновое задание и дадим пользователю кнопку "Отмена". Разберем по шагам этот алгоритм:

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

Этот способ позволяет загружать данные в разы быстрее (так как работа идет непосредственно на сервере в фоне) и предоставляет пользователю современный, понятный интерфейс.

Способ 3: Использование встроенного прерывания (Ctrl+Break)

Если переписывать логику под фоновые задания сейчас нет времени, а прерывать цикл на клиенте все-таки нужно, рассмотрим простейший встроенный механизм платформы — ОбработкаПрерыванияПользователя().

Этот метод проверяет, не нажал ли пользователь сочетание клавиш Ctrl+Break (или Ctrl+Cancel). Если нажал — вызывается исключение и выполнение кода прекращается.

Посмотрим на пример использования:


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

Преимущества этого способа: мы избавляемся от раздражающих диалоговых окон ("Продолжить?"), код выполняется без интерактивных пауз до самого конца. Нам достаточно лишь проинструктировать пользователя: "Если хотите остановить загрузку досрочно — нажмите Ctrl+Break на клавиатуре". Важно помнить, что этот метод работает не во всех клиентских контекстах, но в обычных клиентских процедурах формы он отрабатывает корректно.

Итоги

Подведем итог. Если вы разрабатываете загрузку данных в современных версиях 1С, избегайте диалоговых окон внутри массивных циклов. Если требуется прерывание:

  1. Для простых сценариев используйте Асинх/Ждать с асинхронным диалогом (доступно с 8.3.18).
  2. Для тяжелых и долгих загрузок всегда переводите процесс в фоновое задание с прогресс-баром и кнопкой отмены через механизмы БСП. Это обеспечит максимальную производительность.
  3. Для быстрых обходных путей вставьте в цикл вызов ОбработкаПрерыванияПользователя() и научите операторов использовать горячие клавиши.
← На главную