При разработке расширений для платформы 1С:Предприятие 8.3 программисты часто сталкиваются с загадочным поведением отладчика: код внутри процедуры (например, цикл обработки строк) выполняется полностью, управление доходит до конца метода, а затем система внезапно совершает «прыжок» назад и начинает выполнять ту же самую процедуру повторно. Рассмотрим подробнее, почему возникает этот эффект «двойного захода», разберем по шагам причины его появления и выясним, как правильно организовать код, чтобы избежать ошибок и лишней нагрузки на систему.
Типичный сценарий проблемы выглядит так: создается расширение для существующей формы документа или справочника. Программист захватывает обработчик события ПриОткрытии и использует аннотацию &После (&After). Внутри размещается простой код, например, заполнение колонки в табличной части:
Процедура Расш1_ПриОткрытииПосле(Отказ)
Для каждого итератор из Объект.Товары Цикл
итератор.ПроцентПрибыли = 5;
Сумма = Сумма + итератор.ПроцентПрибыли;
КонецЦикла;
КонецПроцедуры
Проанализируем ситуацию через отладчик. Мы видим, что цикл отрабатывает штатно, но после строки КонецПроцедуры стек вызовов не очищается, а процедура запускается вновь. Это происходит не из-за ошибки в самом цикле, а из-за того, что само событие вызывается платформой или конфигурацией дважды.
Основными причинами такого поведения являются:
ПодключаемыеКомандыКлиент.НачатьОбновлениеКоманд инициирует обновление интерфейса формы. Это может приводить к повторному срабатыванию цепочки событий при открытии формы.ПриОткрытии выполняет действия, требующие обновления контекста формы, клиентская часть может «перерисовать» форму, что визуально в отладчике выглядит как повторный вход.&После. В этом случае система честно выполнит её два раза.Прежде чем исправлять двойной заход, разберем, почему сам подход изменения данных в событии ПриОткрытии является некорректным с точки зрения архитектуры 1С. Посмотрим на пример кода выше. Программист устанавливает ПроцентПрибыли = 5 для каждой строки при каждом открытии формы.
Во-первых, это событие происходит на клиенте. Изменение данных объекта (Объект.Товары) приводит к тому, что форма сразу помечается как модифицированная (свойство Модифицированность = Истина). Пользователь только открыл документ, ничего не менял, но при закрытии система настойчиво предложит сохранить изменения.
Во-вторых, клиентские циклы по большим табличным частям работают медленнее серверных из-за необходимости постоянной синхронизации данных между тонким клиентом и сервером. Если строк будет много, открытие формы превратится в мучение для пользователя.
Для того чтобы избежать проблем с двойным вызовом на клиенте и повысить производительность, мы рекомендуем перенести код из клиентского события ПриОткрытии в серверные события. Выясним, какое событие подходит лучше всего:
Пример переработки кода для расширения:
&НаСервере
&После("ПриСозданииНаСервере")
Процедура Расш1_ПриСозданииНаСервереПосле(Отказ, СтандартнаяОбработка)
// Код выполняется один раз на сервере
Для каждого СтрокаТовар из Объект.Товары Цикл
СтрокаТовар.ПроцентПрибыли = 5;
КонецЦикла;
// Суммирование лучше делать через агрегатные функции ТЧ или Итог()
КонецПроцедуры
Если по каким-то специфическим причинам вам жизненно необходимо оставить код именно в клиентском событии ПриОткрытии, мы можем применить защитный механизм — «флаг выполнения». Рассмотрим, как это реализовать в рамках расширения.
Добавим в модуль формы переменную, которая будет хранить состояние выполнения нашего кода. Важно объявить её так, чтобы она была доступна в контексте модуля формы.
&НаКлиенте
Переменная_ДанныеЗаполнены = Ложь;
&НаКлиенте
&После("ПриОткрытии")
Процедура Расш1_ПриОткрытииПосле(Отказ)
Если Переменная_ДанныеЗаполнены Тогда
Возврат; // Если уже заходили сюда, выходим сразу
КонецЕсли;
// Выполняем полезную нагрузку
Для каждого итератор из Объект.Товары Цикл
итератор.ПроцентПрибыли = 5;
КонецЦикла;
// Устанавливаем флаг, что код отработал
Переменная_ДанныеЗаполнены = Истина;
КонецПроцедуры
Этот метод гарантирует, что даже если платформа по какой-то причине (например, из-за инициализации подключаемых команд БСП) вызовет обработчик события повторно, ваш тяжелый цикл или логика расчетов не запустятся второй раз.
Если вы уверены, что логика должна работать без флагов, проанализируем ситуацию глубже. Иногда причиной «фантомных» вызовов становится забагованный кэш метаданных расширений. Рекомендуем выполнить следующие действия:
НачатьОбновлениеКоманд, то это штатное поведение БСП, и вам нужно использовать Решение №1 или №2.Расш1_ПриОткрытииПосле. Если оно там есть, и при этом у процедуры стоит аннотация &После, то вы сами создали двойную привязку.Эффект «двойного захода» в цикл — это чаще всего не ошибка платформы, а следствие работы сложных механизмов типовых конфигураций. Посмотрим на ключевые выводы нашего разбора:
ПриСозданииНаСервере), они работают стабильно, быстро и вызываются строго один раз.Соблюдение этих правил сделает ваш код в расширениях более надежным, а работу пользователей с программой — более комфортной и быстрой.