При обработке больших объемов данных (например, при загрузке прайс-листов на 80 000 позиций) разработчики 1С часто сталкиваются с ситуацией, когда процедура на сервере выполняется слишком долго, а пользователь теряет контроль над процессом. Попытка вызвать интерактивные методы или обработать нажатие кнопки прямо во время исполнения серверного кода приводит к ошибкам. Разберем подробнее, почему так происходит, какие существуют технические ограничения платформы и как правильно организовать управляемый выход из длительной процедуры.
Выясним причину, по которой обычный клик по кнопке формы не останавливает код на сервере. При синхронном вызове серверной процедуры клиентский сеанс полностью замирает в ожидании ответа от сервера. Любое интерактивное взаимодействие с пользователем блокируется.
Также важно помнить об ограничениях контекста:
ОбработкаПрерыванияПользователя() доступен только в клиентских контекстах (тонкий клиент, веб-клиент, толстый клиент). Вызов этой функции на сервере вызовет ошибку компиляции или выполнения.Наиболее правильным и технологичным решением является перенос длительного процесса в фоновое задание. В этом случае серверный код выполняется асинхронно, а интерфейс формы остается активным для пользователя.
Рассмотрим пошаговый алгоритм реализации этого метода:
ДлительныеОперации.ОтменитьВыполнениеЗадания() или метода ФоновыеЗадания.Остановить().Проанализируем пример схемы работы фонового задания с использованием Библиотеки Стандартных Подсистем (БСП):
&НаКлиенте
Процедура ЗапуститьЗагрузку(Команда)
ПараметрыПроцедуры = Новый Структура("ПутьКФайлу", ПутьКФайлу);
ДлительнаяОперация = ВыполнитьЗагрузкуВФонеНаСервере(ПараметрыПроцедуры);
// Подключаем опрос состояния
ПодключитьОбработчикОжидания("ПроверитьЗавершениеЗагрузки", 2);
КонецПроцедуры
&НаСервере
Функция ВыполнитьЗагрузкуВФонеНаСервере(ПараметрыПроцедуры)
Возврат ДлительныеОперации.ВыполнитьВФоне("МойМодуль.ЗагрузитьПрайсЛист", ПараметрыПроцедуры);
КонецФункции
&НаКлиенте
Процедура ОтменитьЗагрузку(Команда)
ОтменитьЗаданиеНаСервере(ИдентификаторЗадания);
ОтключитьОбработчикОжидания("ПроверитьЗавершениеЗагрузки");
Сообщить("Загрузка прервана пользователем.");
КонецПроцедуры
Если нет возможности использовать фоновые задания, разберем альтернативный способ — порционную обработку. Суть метода заключается в том, что массив данных разбивается на небольшие блоки (например, по 500–1000 строк), и управление периодически возвращается на клиент.
Проанализируем алгоритм работы:
ОбработкаПрерыванияПользователя(), который отслеживает нажатие комбинации клавиш Ctrl + Break или нажатие кнопки отмены.Посмотрим на пример организации порционного вызова:
&НаКлиенте
Процедура ОбработатьПорциями()
ВсегоСтрок = МассивДанных.Количество();
РазмерПорции = 1000;
Индекс = 0;
Пока Индекс < ВсегоСтрок Цикл
// Проверяем, не нажал ли пользователь отмену
ОбработкаПрерыванияПользователя();
// Формируем порцию для отправки на сервер
Порция = ПолучитьПорцию(МассивДанных, Индекс, РазмерПорции);
ОбработатьПорциюНаСервере(Порция);
Индекс = Индекс + РазмерПорции;
КонецЦикла;
КонецПроцедуры
Еще один вариант взаимодействия с длительной процедурой — использование промежуточного хранилища состояний (константы, регистра сведений или хранилища настроек). Процедура на сервере в цикле периодически проверяет значение флага (например, каждые 500 элементов). Если флаг установлен в значение Истина, серверный процесс корректно завершает работу через оператор Прервать или Возврат. Установка самого флага отмены при этом выполняется из другого сеанса.
Выполнение загрузки 80 000 строк в течение 9 часов указывает на узкие места в алгоритме обработки данных. Посмотрим, за счет чего можно сократить время выполнения в десятки раз:
Excel.Application работает крайне медленно и потребляет много ресурсов. Для чтения XLSX-файлов эффективнее использовать ЧтениеXML, метод ТабличныйДокумент.Прочитать() или выгрузку в формат CSV с последующим чтением через ЧтениеТекста.Объект.ОбменДанными.Загрузка = Истина. Это отключает лишние проверки и подписки на события при массовом создании объектов.МенеджерЗаписи применяйте РегистрСведенийНаборЗаписей. Формируйте набор на группу элементов и записывайте его целиком.НачатьТранзакцию() ... ЗафиксироватьТранзакцию(). Это кардинально снижает количество обращений к СУБД на запись дисковых данных.