При разработке и сопровождении управляемых форм в платформе 1С:Предприятие 8.3 разработчики нередко сталкиваются с ситуацией: в коде обработчика события вызывается серверная процедура, при пошаговой отладке весь серверный код выполняется мгновенно, однако при переходе от строки КонецПроцедуры обратно в клиентскую точку вызова интерфейс «замирает» на 10–20 секунд. Замер производительности при этом относит практически 100% времени выполнения именно к вызову этой процедуры.
Разберем детально, почему возникает такое поведение, как устроена работа контекста управляемой формы и как правильно оптимизировать вызовы, чтобы исключить задержки интерфейса.
Рассмотрим типовой пример обработчика изменения счета в строке таблицы, где проявляется данное зависание:
&НаКлиенте
Процедура НастройкаСчетДтПриИзменении(Элемент)
ТД = Элементы.Настройка.ТекущаяСтрока;
СтрокаТаблицы = Элементы.Настройка.ТекущиеДанные;
УстановитьЗаголовкиИДоступностьСубконто(ЭтаФорма, СтрокаТаблицы.СчетДт, "", "Дт", Истина);
ДанныеСчета = БухгалтерскийУчетВызовСервераПовтИсп.ПолучитьСвойстваСчета(СтрокаТаблицы.СчетКт);
Для Индекс = 1 По 3 Цикл
Если Индекс <= ДанныеСчета.КоличествоСубконто Тогда
СтрокаТаблицы["СубконтоДт" + Индекс] = Новый(ДанныеСчета["ВидСубконто" + Индекс + "ТипЗначения"].Типы()[0]);
КонецЕсли;
КонецЦикла;
ПоляОбъекта = Новый Структура("Субконто1, Субконто2, Субконто3",
"СубконтоДт1", "СубконтоДт2", "СубконтоДт3");
БухгалтерскийУчетКлиентСервер.ПриИзмененииСчета(СтрокаТаблицы.СчетДт, СтрокаТаблицы, ПоляОбъекта, Истина);
// Проблемный контекстный вызов:
СчетПриИзменении(ТД, "Дт", "Настройка", "СчетДт", СтрокаТаблицы.СчетДт);
КонецПроцедуры
Когда мы объявляем серверную процедуру с директивой &НаСервере (контекстный вызов), платформа выполняет не просто передачу указанных в скобках аргументов. Происходит следующий цикл операций:
rphost).Важный момент: ни отладчик, ни стандартный серверный замер производительности не показывают сетевые издержки и затраты тонкого клиента на разбор полученных данных. В интерфейсе отладчика курсор останавливается на последней строке серверной процедуры, после чего процесс «повисает» именно на этапе возврата управления в клиентский поток. Если форма содержит десятки тысяч строк, тяжелые таблицы соответствий проводок или динамически настраиваемые элементы субконто, задержка может составлять десятки секунд.
Рассмотрим основные способы устранения этой проблемы.
Проанализируем логику процедуры: если для ее выполнения не требуется читать или модифицировать всю форму целиком, необходимо отказаться от директивы &НаСервере. В процедуру &НаСервереБезКонтекста передаются исключительно те параметры, которые нужны для вычисления, а результат возвращается через возвращаемое значение функции или структуру.
Посмотрим на пример рефакторинга:
&НаКлиенте
Процедура НастройкаСчетДтПриИзменении(Элемент)
СтрокаТаблицы = Элементы.Настройка.ТекущиеДанные;
// Формируем компактную структуру параметров
ПараметрыОбработки = Новый Структура;
ПараметрыОбработки.Вставить("СчетДт", СтрокаТаблицы.СчетДт);
ПараметрыОбработки.Вставить("ИмяКолонки", "СчетДт");
ПараметрыОбработки.Вставить("Префикс", "Дт");
// Вызываем серверную функцию без передачи формы
Результат = ПолучитьПараметрыСчетаБезКонтекста(ПараметрыОбработки);
// Применяем результат локально на клиенте
СтрокаТаблицы.СубконтоДт1 = Результат.ЗначениеПоУмолчанию1;
СтрокаТаблицы.СубконтоДт2 = Результат.ЗначениеПоУмолчанию2;
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПолучитьПараметрыСчетаБезКонтекста(ПараметрыОбработки)
Результат = Новый Структура("ЗначениеПоУмолчанию1, ЗначениеПоУмолчанию2");
// Выполняем выборку данных из базы без обращения к реквизитам формы
// ...
Возврат Результат;
КонецФункции
При таком подходе размер передаваемых по сети пакетов сокращается с нескольких мегабайт до сотен байт, а десериализация контекста на клиенте полностью исключается.
Если в обработке загрузки данных (например, при выгрузке проводок из внешних систем) табличная часть используется как промежуточный буфер на десятки тысяч строк, хранение ее в реквизите ДанныеФормыКоллекция перегружает любой контекстный вызов.
Разберем правильный алгоритм оптимизации структуры формы:
АдресХранилища = ПоместитьВоВременноеХранилище(ТаблицаДанных, ЭтаФорма.УникальныйИдентификатор);.АдресХранилища.При возврате с сервера клиентское приложение обновляет строку таблицы. Это обновление может неявно вызвать события ПриАктивизацииСтроки, ПриАктивизацииЯчейки или пересчет сложных правил условного оформления.
Если в обработчике ПриАктивизацииСтроки прописаны дополнительные вызовы сервера, они встают в очередь и создают эффект «двойного» зависания. Разберем, как временно блокировать такие обработчики с помощью флага:
&НаКлиенте
Перем мПропускатьСобытияАктивизации;
&НаКлиенте
Процедура НастройкаСчетДтПриИзменении(Элемент)
мПропускатьСобытияАктивизации = Истина;
Попытка
// Вызов сервера
СчетПриИзмененииСервер();
Окончательно
мПропускатьСобытияАктивизации = Ложь;
КонецПопытки;
КонецПроцедуры
&НаКлиенте
Процедура НастройкаПриАктивизацииСтроки(Элемент)
Если мПропускатьСобытияАктивизации = Истина Тогда
Возврат;
КонецЕсли;
// Обычная логика активизации
КонецПроцедуры
Нередко одна и та же обработка на одном сервере работает быстро в тестовой базе, но зависает в рабочей. Это может быть связано с накоплением пользовательских настроек формы или повреждением кэша тонкого клиента.
Проверим и применим следующие действия:
/ClearCache либо вручную очистить каталоги временных файлов пользователя %LocalAppData%\1C\1Cv8 и %AppData%\1C\1Cv8.Главное правило клиент-серверной разработки на управляемых формах: контекстные процедуры &НаСервере должны использоваться только в тех случаях, когда требуется неизбежная модификация большинства реквизитов формы. Во всех остальных сценариях работа строится через &НаСервереБезКонтекста с явной передачей минимального набора параметров.