При разработке сложных интерфейсов в обычном приложении 1С (толстый клиент), таких как «Рабочее место менеджера» или «Монитор заказов», разработчики часто сталкиваются с неприятным эффектом. На форме размещено несколько табличных полей (списков документов или справочников), и для одного из них включено стандартное автообновление. Однако при срабатывании таймера платформа инициирует обновление всех списков на форме, что приводит к лавинообразному вызову тяжелых процедур ПриПолученииДанных и ПриВыводеСтроки. В результате интерфейс «замирает», а нагрузка на сервер и клиентскую часть резко возрастает.
В этой статье мы подробно разберем, почему платформа ведет себя именно так, проанализируем технические причины и выработаем комплекс мер по оптимизации производительности таких форм.
Прежде всего, выясним причину такого поведения. В обычном приложении графический интерфейс тесно связан с операционной системой через Win32 API. Когда один из элементов управления (например, ДокументСписок) сообщает системе, что его данные изменились и их нужно перерисовать, платформа часто инициирует цикл перерисовки всей клиентской области формы.
Рассмотрим ключевые факторы, влияющие на это:
WM_PAINT всей форме. Платформа 1С в ответ на это проверяет актуальность данных во всех видимых элементах.Таким образом, срабатывание события ПриПолученииДанных на «соседнем» списке при обновлении основного — это не баг конкретной версии, а заложенная логика работы толстого клиента.
Разберем подробнее, почему именно эти события становятся узким местом. Процедура ПриВыводеСтроки вызывается для каждой ячейки каждой строки, попадающей в видимую область. Если у вас 20 списков и форма перерисовывается раз в 5–10 секунд, количество вызовов исчисляется тысячами.
Процедура ПриПолученииДанных более эффективна, так как она работает с массивом строк (оформлений), но и она может стать критическим фактором, если внутри выполняются:
ОформлениеСтроки.ДанныеСтроки.Ссылка.Склад.Ответственный).Проанализируем ситуацию: если платформа дергает эти процедуры каждые несколько секунд для всех списков сразу, даже если данные в них не менялись, мы получаем бесполезную нагрузку на систему.
Самый эффективный способ борьбы с этой проблемой — полный отказ от стандартного свойства «Автообновление» в настройках табличного поля. Вместо него мы будем использовать программный метод ПодключитьОбработчикОжидания(). Это позволит нам полностью контролировать процесс.
Рассмотрим пример реализации. В модуле формы определим процедуру обновления:
Процедура ПриОткрытии()
// Вместо стандартного автообновления раз в 60 секунд
ПодключитьОбработчикОжидания("ОбновитьДанныеСписков", 60);
КонецПроцедуры
Процедура ОбновитьДанныеСписков() Экспорт
// Мы можем обновлять списки выборочно
ЭлементыФормы.СписокЗаказов.Обновить();
// Или вводить условия, например, обновлять только если страница активна
Если ЭлементыФормы.ОсновнаяПанель.ТекущаяСтраница = ЭлементыФормы.ОсновнаяПанель.Страницы.Склад Тогда
ЭлементыФормы.СписокОстатков.Обновить();
КонецЕсли;
КонецПроцедуры
Этот подход позволяет избежать одновременного удара по производительности со стороны всех списков сразу. Мы можем разнести их обновление во времени, используя разные обработчики с разными интервалами.
Если мы не можем избежать вызова события, мы должны сделать его максимально «легким». Основное правило: никаких запросов в цикле. Посмотрим на правильный паттерн использования ПриПолученииДанных:
Процедура СписокПриПолученииДанных(Элемент, ОформленияСтрок)
// 1. Собираем все нужные ссылки в массив
МассивСсылок = Новый Массив;
Для Каждого Оформление Из ОформленияСтрок Цикл
МассивСсылок.Добавить(Оформление.ДанныеСтроки.Ссылка);
КонецЦикла;
// 2. Выполняем ОДИН запрос ко всем данным сразу
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ Ссылка, Состояние ИЗ РегистрСведений.СтатусыДокументов ГДЕ Ссылка В (&МассивСсылок)";
Запрос.УстановитьПараметр("МассивСсылок", МассивСсылок);
ТаблицаРезультатов = Запрос.Выполнить().Выгрузить();
ТаблицаРезультатов.Индексы.Добавить("Ссылка");
// 3. Распределяем данные по строкам
Для Каждого Оформление Из ОформленияСтрок Цикл
СтрокаДанных = ТаблицаРезультатов.Найти(Оформление.ДанныеСтроки.Ссылка, "Ссылка");
Если СтрокаДанных <> Неопределено Тогда
Оформление.Ячейки.Статус.УстановитьТекст(СтрокаДанных.Состояние);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Использование индексированной таблицы значений или соответствия внутри процедуры позволяет мгновенно находить нужные данные, минимизируя время блокировки интерфейса.
Выясним еще один важный момент: часто данные, которые выводятся в списках, не меняются так часто, как происходит перерисовка. Мы можем внедрить механизм кэширования. Создадим переменную модуля формы мКэшДанных (типа Соответствие) и будем хранить в ней результаты расчетов.
Рассмотрим логику:
ПриПолученииДанных проверяем, есть ли данные в кэше и не устарели ли они.Это кардинально снижает нагрузку, даже если платформа вызывает обновление списка каждые 5 секунд из-за соседнего элемента.
Проанализируем еще несколько приемов, которые помогут ускорить работу тяжелых форм:
ДокументСписок всегда устанавливайте жесткий отбор по периоду. Если список пытается проинициализировать тысячи документов за весь период работы компании, любые события отрисовки будут тормозить из-за работы индексов БД.ТаблицаЗначений. Заполняйте её при открытии и обновляйте по кнопке. Таблица значений в памяти клиента не вызывает системных событий обновления при перерисовке других элементов формы.Элемент.Обновить() проверяйте, виден ли этот элемент пользователю в данный момент (не скрыт ли он на другой вкладке панели).Поведение 1С в обычном приложении, при котором обновление одного списка инициирует перерисовку всей формы, является фундаментальной особенностью платформы. Чтобы интерфейс оставался отзывчивым, рекомендуем придерживаться следующей стратегии: отказ от стандартного автообновления в пользу ПодключитьОбработчикОжидания, групповая обработка данных в событии ПриПолученииДанных и минимизация количества списков на одной форме. В долгосрочной перспективе лучшим решением станет переход на Управляемые формы, где механизм ДинамическийСписок работает гораздо оптимальнее и изолированнее.