Как ускорить открытие формы элемента в 1С, если оно тормозит из-за большого количества кода в "ПриОткрытии"?

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

В процессе доработки конфигураций 1С, особенно на обычных формах (например, в "Комплексной автоматизации 1.1"), часто возникает задача добавить на форму множество элементов для удобства пользователей. Со временем количество таких доработок растет, и форма начинает открываться все медленнее. В этой статье разберем ситуацию, когда открытие карточки контрагента замедлилось в два раза из-за многочисленных процедур, управляющих видимостью элементов, и рассмотрим эффективные способы оптимизации.

Анализ проблемы: почему форма открывается медленно?

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

Рассмотрим фрагмент кода, который приводит к замедлению:

1. Основной цикл в процедуре ПриОткрытии

Этот код итерируется по таблице сервисов и динамически выполняет процедуры для каждого из них.


// ... кусок из ПриОткрытии
ЗаполнитьТаблицуСервисов; 
Для Каждого Сервис Из тз_Сервисы Цикл
    // ... пропущено для краткости

    // У сервисов "ПФотч", "Бухфон", "СопровождениеWebСервисов" нет реквизита "СопровождаетКонкурент"
    Если НЕ (Сервис.Значение = "ПФотч" ИЛИ Сервис.Значение = "Бухфон_" ИЛИ Сервис.Значение = "СопровождениеWebСервисов_" ИЛИ Сервис.Значение = "ВиженСофт_") Тогда
        Попытка
            Выполнить(Сервис.Значение + "СопровождаетКонкурентПриИзменении(Неопределено)");
        Исключение;
        КонецПопытки;
    КонецЕсли;
    
    // ... другие вызовы
Цикл;

2. Пример вызываемой процедуры

Для каждого сервиса выполняется объемная процедура, которая изменяет свойства видимости для множества элементов.


Процедура Диадок2_СопровождаетКонкурентПриИзменении(Элемент)
    //если программу сопровождает конкурент становится видимым поле Конкурент,
    //а не нужные для заполнения поля исчезают.
    Если Диадок2_СопровождаетКонкурент = Истина Тогда
        ЭлементыФормы.РамкаГруппы20.ЦветРамки = ЦветаСтиля.ЦветОсобогоТекста;
        ЭлементыФормы.НадписьДиадок2_Конкурент.Видимость = истина;
        ЭлементыФормы.Диадок2_Конкурент.Видимость = истина;
        ЭлементыФормы.Диадок2_УслугаПредоставлена.Видимость = ложь;
        ЭлементыФормы.Диадок2_ДатаОконДействСертиф.Видимость = ложь;
        ЭлементыФормы.НадписьДиадок2_ДатаОконДействСертиф.Видимость = ложь;
        // ... и еще много-много строк изменения видимости
    Иначе
        ЭлементыФормы.РамкаГруппы20.ЦветРамки = ЦветаСтиля.ТекстИнформационнойНадписи;
        ЭлементыФормы.Диадок2_Конкурент.Видимость = ложь;
        ЭлементыФормы.НадписьДиадок2_Конкурент.Видимость = ложь;
        ЭлементыФормы.Диадок2_УслугаПредоставлена.Видимость = истина;
        // ... и снова много строк
    КонецЕсли;
КонецПроцедуры

Основные причины низкой производительности здесь:

  1. Многократное изменение свойств элементов. Каждая строка вида ЭлементыФормы.ИмяЭлемента.Видимость = Ложь — это отдельная операция. Когда таких строк десятки, а процедура вызывается в цикле 40 раз, общее число операций становится огромным, что сильно нагружает механизм отрисовки формы.
  2. Использование Выполнить(). Динамическое выполнение кода через процедуру Выполнить() — это мощный, но медленный механизм. Платформе требуется дополнительное время на синтаксический анализ и компиляцию передаваемой строки. В циклах это является классическим антипаттерном.
  3. Возможные неявные серверные вызовы. Условие Если Диадок2_СопровождаетКонкурент = Истина Тогда обращается к реквизиту объекта. Если данные этого объекта еще не были полностью считаны с сервера, каждое такое обращение внутри цикла может инициировать неявный запрос к базе данных, что катастрофически сказывается на производительности в клиент-серверном режиме.

Теперь давайте разберем, как можно оптимизировать этот код.

Решение 1: Группировка элементов управления на панелях

Это самый простой и часто самый эффективный способ. Вместо того чтобы управлять видимостью каждого отдельного поля, надписи и флажка, их следует сгруппировать.

Как это сделать:

  1. В конфигураторе откройте форму элемента.
  2. Для каждого "сервиса" создайте элемент ГруппаФормы или Панель.
  3. Перенесите все элементы, относящиеся к одному сервису (например, все, что начинается с "Диадок2_..."), внутрь соответствующей группы.
  4. Теперь в коде вам нужно управлять видимостью только одного элемента — самой группы.

Пример "до" и "после":

До оптимизации:


ЭлементыФормы.НадписьДиадок2_Конкурент.Видимость = истина;
ЭлементыФормы.Диадок2_Конкурент.Видимость = истина;
ЭлементыФормы.Диадок2_УслугаПредоставлена.Видимость = ложь;
ЭлементыФормы.Диадок2_ДатаОконДействСертиф.Видимость = ложь;
// и т.д.

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

Предположим, мы создали группу ГруппаДиадок2_Конкурент и ГруппаДиадок2_Основная.


Если Диадок2_СопровождаетКонкурент = Истина Тогда
    ЭлементыФормы.ГруппаДиадок2_Конкурент.Видимость = Истина;
    ЭлементыФормы.ГруппаДиадок2_Основная.Видимость = Ложь;
Иначе
    ЭлементыФормы.ГруппаДиадок2_Конкурент.Видимость = Ложь;
    ЭлементыФормы.ГруппаДиадок2_Основная.Видимость = Истина;
КонецЕсли;

В результате вместо десятков команд на изменение свойств элементов мы выполняем всего две. Это радикально сокращает время на перерисовку формы.

Решение 2: Получение всех данных одним запросом

Как мы уже выяснили, обращения к реквизитам объекта внутри цикла могут вызывать неявные запросы к базе данных. Чтобы этого избежать, необходимо получить все нужные данные для всех 40 сервисов одним большим запросом до начала цикла.

Порядок действий:

  1. Сформируйте текст запроса, который получит значения всех необходимых флагов (СопровождаетКонкурент, Отключен и т.д.) для текущего контрагента. Скорее всего, эти данные хранятся в регистрах сведений или табличных частях.
  2. Выполните этот запрос один раз перед циклом.
  3. Выгрузите результат в ТаблицуЗначений или Структуру.
  4. Внутри цикла обращайтесь уже не к реквизитам объекта (что может вызвать запрос к БД), а к данным из этой таблицы значений, которая уже находится в памяти.

Такой подход гарантирует, что при настройке видимости элементов не будет никаких "сюрпризов" в виде незапланированных обращений к серверу. Вы полностью контролируете процесс получения данных.

Решение 3: Рефакторинг кода и отказ от `Выполнить()`

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

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

Пример рефакторинга:


// Вместо цикла с Выполнить()
Для Каждого Сервис Из тз_Сервисы Цикл
    // Получаем значения флагов из заранее подготовленной структуры/таблицы
    ЗначениеСопровождаетКонкурент = ДанныеСервисов[Сервис.Значение].СопровождаетКонкурент;
    ЗначениеОтключен = ДанныеСервисов[Сервис.Значение].Отключен;

    // Вызываем единую процедуру
    НастроитьВидимостьГруппыСервиса(Сервис.Значение, ЗначениеСопровождаетКонкурент, ЗначениеОтключен);
Цикл;


// Новая универсальная процедура
Процедура НастроитьВидимостьГруппыСервиса(Префикс, СопровождаетКонкурент, Отключен)
    
    // Здесь логика, которая использует Префикс для обращения к элементам
    // Например, через коллекцию ЭлементыФормы по индексу
    ЭлементыФормы[Префикс + "_Конкурент"].Видимость = СопровождаетКонкурент;
    ЭлементыФормы["Надпись" + Префикс + "_Конкурент"].Видимость = СопровождаетКонкурент;
    
    // и т.д.
    
КонецПроцедуры

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

Инструменты для диагностики

Не забывайте, что главный инструмент для поиска узких мест — это "Замер производительности". Именно он помог автору темы локализовать проблему.

Совет: при анализе замера производительности попробуйте отключать флажок "Для вызова процедур и функций включать время выполнения". Это позволит увидеть, сколько времени тратится на сам вызов, а сколько — на выполнение кода внутри вызываемой процедуры. Это может дать дополнительную информацию для анализа.

В заключение, отметим, что оптимизация — это комплексная задача. Начните с группировки элементов на форме, затем оптимизируйте получение данных с сервера и приведите код в порядок, избавившись от медленных конструкций. Эти шаги помогут значительно ускорить открытие даже самых "тяжелых" и доработанных форм.

← На главную