Обработка на сервере с индикатором (упр. приложение)




Обработка на сервере с отображением процесса загрузки и возможностью прерывания

&НаСервере
Функция ИнициализацияОбработкиСервер()

    Запрос = Новый Запрос;
   
Запрос.Текст =
   
«ВЫБРАТЬ
    |   Спр.Ссылка
    |ИЗ
    |   Справочник.Банки КАК Спр»
;

    Данные      = Запрос.Выполнить().Выгрузить();
   
АдресДанных = ПоместитьВоВременноеХранилище(Данные, УникальныйИдентификатор);

    // Обработка
    // В справочнике ~ 4000 элементов

    Возврат Данные.Количество();

КонецФункции

&НаСервереБезКонтекста
Функция ОбработатьЭлемент(ДанныеЭлемента)

    СпрОбъект = ДанныеЭлемента.Ссылка.ПолучитьОбъект();

    // Обработка

    СпрОбъект.ОбменДанными.Загрузка = Истина;
   
СпрОбъект.Записать();

КонецФункции

&НаСервереБезКонтекста
Функция ОбработатьПорциюНаСервере(Начало, Конец, АдресДанных)

    Данные = ПолучитьИзВременногоХранилища(АдресДанных);

    Для Счетчик = Начало 1 По Конец 1 Цикл

        ОбработатьЭлемент(Данные[Счетчик]);

    КонецЦикла;

КонецФункции

&НаКлиенте
Процедура ВыполнитьОбработку(Команда)

    Количество      = ИнициализацияОбработкиСервер();
   
РазмерШага      = 10;
   
КоличествоШагов = Цел(Количество / РазмерШага);
   
КоличествоЦел   = КоличествоШагов * РазмерШага;

    Если КоличествоЦел < Количество Тогда
       
Элементы.Индикатор.МаксимальноеЗначение = КоличествоШагов + 1;
    Иначе
       
Элементы.Индикатор.МаксимальноеЗначение = КоличествоШагов;
    КонецЕсли;

    Для Счетчик = 1 По КоличествоШагов Цикл

        ОбработатьПорциюНаСервере(РазмерШага * (Счетчик 1) + 1, РазмерШага * Счетчик, АдресДанных);
       
Индикатор = Счетчик;
       
ОбработкаПрерыванияПользователя();
       
ОбновитьОтображениеДанных();

    КонецЦикла;

    Если КоличествоЦел < Количество Тогда

        ОбработатьПорциюНаСервере(КоличествоШагов * РазмерШага + 1, Количество, АдресДанных);
       
Индикатор = Индикатор + 1;

    КонецЕсли;

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

15 Comments

  1. acsent

    Думал, что самая затратная прцедура будет

    Данные = ПолучитьИзВременногоХранилища(АдресДанных);

    а оказалось, вызов сервера куда более затратен

    Reply
  2. Evg-Lylyk

    Приблизительно о том же писал здесь http://infostart.ru/public/71407/

    Reply
  3. acsent

    Основная фишка в том, чтобы получить выборку данных на сервере и не передавать ее на клиент

    Reply
  4. Evg-Lylyk

    (3) Согласен это важно. Разумно осуществлять индикацию раз в секунду тогда будет меньше вызовов сервера… упростится код

    Reply
  5. Mopo3

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

    Reply
  6. Yury1001

    Отлично работает с выборкой результата запроса – ничего не сбивается.

    Вызов сервера действительно затратный по времени, если делить это тысячи раз, будет ощутимо, и потому можно реализовать возврат по таймеру, ну там раз в 3 секунды, например.

    Reply
  7. Den_D

    Мне кажется к данной проблеме нет однозначного подхода и универсального решения.

    Однако какие-то решения в определенных условиях являются лучшими.

    Reply
  8. juntatalor

    Раньше реализовывал индикацию данных также, как у вас.

    Плюсы: точность отображения, легкий вывод сообщений на сервере, общее удобство

    Минусы:

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

    Сейчас перевел на работу в фоновых заданиях. У них тоже есть свои минусы: запускаются дольше, сложнее получать из них информацию, сложнее отладка, степень индикации не очень точна. Однако, там можно использовать Запрос.Выполнить().Выбрать(), да и в целом чисто «серверный» код без «дерганий» клиент<->сервер работает быстрее.

    И насчет Данные = ПолучитьИзВременногоХранилища(АдресДанных) — они скорее всего кешируются в памяти, поэтому и вызов быстрый.

    Reply
  9. KillerMann

    (5) Mopo3, идея у автора хорошая. А вот Вашу не поддерживаю. Каким образом отработает обработчик ожидания на клиенте если на серверной части будет крутиться процедура(функция) обработки результата запроса? Если знаете решение, то пример в студию, только заведомо рабочий. А пока минус за ответ, т.к. считаю ответ вводящий в заблуждение.

    Reply
  10. acsent

    (5) Чтоб подключить обработчик нужно запускать фоновое задание. А чтоб его запускать нужно изменять конфигурацию

    Reply
  11. KillerMann

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

    Reply
  12. E_x

    Благодарю пользователя, это то, что надо. Немного адаптировал под работу со стандартным методом Состояние() и вообще огонь. Еще раз спасибо!

    Reply
  13. Bor_ka

    Попробовал, отлично работает. Спасибо автору.

    Reply
  14. doom2good

    (5) Mopo3,

    Не подскажете, как реализовать через ПодключитьОбработчикОжидания()?

    в моем варианте

    Процедура ВыполнитьОбработку(Команда)
    
    ПодготовитьДанныеСервер();
    МаксимальноеЗначение = КоличествоДанныхВХранилище();
    
    ПодключитьОбработчикОжидания(«ОбновитьСтатус», 1);
    ПодключитьОбработчикОжидания(«ОбработатьДокументы», 1, Истина);
    
    КонецПроцедуры
    

    Показать

    &НаКлиенте
    Процедура ОбновитьСтатус()
    
    ОстатокЭлементов = КоличествоДанныхВХранилище();
    ВсегоЭлементов = МаксимальноеЗначение;
    
    Состояние(«Обработка документов», 100 — ОстатокЭлементов /(ВсегоЭлементов/100), «Документы обрабатываются…»);
    
    Если ОстатокЭлементов = 0 Тогда
    
    ОтключитьОбработчикОжидания(«ОбновитьСтатус»);
    Предупреждение(«Обработка завершена!»);
    
    КонецЕсли;
    
    КонецПроцедуры
    

    Показать

    &НаКлиенте
    Процедура ОбработатьДокументы()
    
    ОбработатьДокументыСервер(АдресХранилищаДанных, АдресХранилищаКоличества);
    
    КонецПроцедуры
    
    &НаСервереБезКонтекста
    Процедура ОбработатьДокументыСервер(АдресХранилищаДанных, АдресХранилищаКоличества)
    
    НепроведенныеДокументы = ПолучитьИзВременногоХранилища(АдресХранилищаДанных);
    
    ВсегоДокументов = НепроведенныеДокументы.Количество();
    
    Для Каждого цДокумент Из НепроведенныеДокументы Цикл
    
    обработатьДокумент(цДокумент);
    
    ВсегоДокументов = ВсегоДокументов — 1;
    ПоместитьВоВременноеХранилище(ВсегоДокументов, АдресХранилищаКоличества);
    
    КонецЦикла;
    
    КонецПроцедуры
    

    Показать

    В этом случае у меня появляется индикатор, но выполнение уходит на сервер и после выполнения индикатор перескакивает с 0% сразу на 100%.

    Reply
  15. TreeDogNight

    (14) doom2good, Меня тоже интересует этот вопрос.Ещё не нашли решение?

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *