Ситуация, когда документ, содержащий всего 100–150 строк, начинает "зависать" при добавлении, удалении или изменении данных, кажется парадоксальной. Для современной платформы 1С такой объем данных является незначительным. Однако на практике разработчики часто сталкиваются с фризами интерфейса. В этой статье мы подробно разберем, почему это происходит, проанализируем технические ошибки и научимся оптимизировать работу с многострочными частями документов.
Прежде чем приступать к исправлению кода, нам необходимо точно выяснить, на что тратится время. Для этого воспользуемся встроенным инструментом Замер производительности. Рассмотрим алгоритм действий:
Проанализируем полученную таблицу. Нас интересуют строки с наибольшим показателем в колонке Число вызовов и Общее время. В 99% случаев виновником оказывается либо обработчик события в модуле формы, либо неявная передача огромного объема данных между клиентом и сервером.
Одной из самых распространенных причин тормозов является процедура ПриАктивизацииСтроки. Это событие срабатывает каждый раз, когда пользователь просто перемещает курсор с одной строки на другую. Если внутри этого обработчика прописан вызов серверной процедуры, каждое движение пользователя превращается в ожидание ответа от сервера.
Разберем, почему это критично. Даже если процедура на сервере выполняется быстро, сама передача данных по сети и процесс сериализации (преобразования данных формы в формат, понятный серверу) занимают значительное время. Выясним причину: при вызове процедуры &НаСервере платформа передает на сервер весь контекст формы, включая всю табличную часть со всеми реквизитами.
Решение: Если вам необходимо выполнять действия при активации строки, старайтесь делать это максимально на клиенте или используйте ПодключитьОбработчикОжидания, чтобы серверный вызов не происходил мгновенно, а давал пользователю возможность пролистать список.
Когда мы работаем с управляемыми формами, важно понимать разницу между &НаСервере и &НаСервереБезКонтекста. Рассмотрим ситуацию: при изменении цены в строке нам нужно получить какую-то информацию из базы данных.
Если мы используем контекстный вызов, 1С передает на сервер всю форму. Для 150 строк это может быть существенный объем данных. Проанализируем пример неоптимального кода:
&НаКлиенте
Процедура ТоварыКоличествоПриИзменении(Элемент)
РассчитатьСуммуСтрокиСервер(); // Контекстный вызов
КонецПроцедуры
&НаСервере
Процедура РассчитатьСуммуСтрокиСервер()
ТекущаяСтрока = Элементы.Товары.ТекущиеДанные;
ТекущаяСтрока.Сумма = ТекущаяСтрока.Количество * ТекущаяСтрока.Цена;
КонецПроцедуры
Для исправления ситуации мы должны использовать вызов без контекста, передавая только необходимые параметры:
&НаКлиенте
Процедура ТоварыКоличествоПриИзменении(Элемент)
ТекущиеДанные = Элементы.Товары.ТекущиеДанные;
ДанныеСтроки = Новый Структура("Количество, Цена", ТекущиеДанные.Количество, ТекущиеДанные.Цена);
Результат = РассчитатьСуммуНаСервере(ДанныеСтроки);
ТекущиеДанные.Сумма = Результат;
КонецПроцедуры
&НаСервереБезКонтекста
Функция РассчитатьСуммуНаСервере(ДанныеСтроки)
Возврат ДанныеСтроки.Количество * ДанныеСтроки.Цена;
КонецФункции
В этом случае на сервер передается только маленькая структура, а не весь документ, что ускоряет работу в разы.
Часто в формах настраивают Условное оформление, чтобы подсветить строки (например, отсутствие товара на складе). Если в правилах оформления используется обращение к реквизитам через "точку" (например, Номенклатура.ТипНоменклатуры), платформа может выполнять неявные запросы к базе данных при отрисовке каждой строки.
Проанализируем, как это работает: система отрисовывает таблицу и для каждой видимой строки лезет в базу, чтобы узнать тип номенклатуры. Это "убивает" производительность интерфейса.
Как исправить: Вынесите необходимые данные для оформления в скрытые колонки самой табличной части. Заполняйте их один раз при открытии документа или при изменении номенклатуры, и настраивайте оформление, опираясь на эти готовые значения в строке.
Посмотрим на классическую ошибку начинающих разработчиков — выполнение запроса внутри цикла по строкам табличной части. Если в документе 150 строк, система выполнит 150 отдельных запросов к СУБД.
// ПРИМЕР ТОГО, КАК ДЕЛАТЬ НЕЛЬЗЯ
&НаСервере
Процедура ПересчитатьЦены(Объект)
Для Каждого СтрокаТЧ Из Объект.Товары Цикл
Запрос = Новый Запрос("ВЫБРАТЬ Цена ИЗ РегистрСведений.Цены.СрезПоследних(..., Номенклатура = &Ном)");
Запрос.УстановитьПараметр("Ном", СтрокаТЧ.Номенклатура);
// ... получение данных ...
КонецЦикла;
КонецПроцедуры
Рассмотрим правильный подход: использование пакетных запросов. Мы должны один раз собрать все ссылки на номенклатуру из табличной части, передать их в один запрос, получить все цены разом и затем распределить их по строкам. Это сокращает время обработки документа с нескольких секунд до миллисекунд.
Если в табличной части отображаются ссылочные поля, 1С при выводе должна сформировать их текстовое представление. Если в конфигурации переопределен обработчик ОбработкаПолученияПредставления для этого объекта и в нем написан тяжелый код (например, получение данных через точку от других связанных объектов), это замедлит отрисовку всей таблицы.
Выясним причину: получение реквизитов через точку внутри таких обработчиков заставляет систему считывать всю таблицу объекта из базы данных. Если строк много, нагрузка на сервер и сеть растет лавинообразно.
Если у вашей таблицы включено отображение подвала с итогами по колонке (Сумма, Количество), платформа пересчитывает эти итоги при каждом изменении любой строки. Для очень больших документов или при наличии сложных программных пересчетов это создает задержку.
Проанализируем решение: в некоторых случаях эффективнее отключить стандартный подсчет итогов в свойствах элемента формы и вызывать процедуру пересчета итогов программно только тогда, когда это действительно нужно (например, при окончании редактирования строки или перед сохранением).
Иногда "тормоза" связаны не с кодом, а с инфраструктурой. Разберем основные моменты:
*.1cd, *.1cl и каталоги временных файлов в исключения.Мы рассмотрели основные причины, по которым документы на 100-150 строк могут работать медленно. Подведем итог: всегда минимизируйте количество серверных вызовов, используйте &НаСервереБезКонтекста, избегайте запросов в циклах и внимательно следите за тем, какие данные система подтягивает через "точку" в интерфейсных событиях. Оптимизация этих моментов позволит сделать работу пользователя комфортной и быстрой.