Как правильно использовать Возврат внутри Попытки и работать с вложенными Попытками в 1С?

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

В процессе разработки во встроенном языке «1С:Предприятия» регулярно возникает необходимость обработки исключительных ситуаций. Для этого используется классическая конструкция Попытка — Исключение — КонецПопытки. Однако при проектировании сложных алгоритмов разработчики часто сталкиваются с неочевидными вопросами: корректно ли использовать оператор Возврат внутри этого блока? Как поведет себя платформа, если одна Попытка вложена в другую? И какие скрытые угрозы таит в себе эта конструкция при работе с транзакциями базы данных?

Давайте подробно разберем эти вопросы, проанализируем архитектурные особенности платформы «1С:Предприятие» и определим лучшие практики написания надежного кода.

1. Оператор Возврат внутри блока Попытка: синтаксис и логика выполнения

Начнем с самого простого и прямого вопроса: «Законно» ли использовать оператор Возврат внутри секции Попытка?

С точки зрения синтаксиса платформы «1С:Предприятие 8» это абсолютно корректно и допустимо. Когда выполнение кода доходит до оператора Возврат внутри блока Попытка, происходит следующее:

  1. Программа мгновенно прерывает выполнение текущей процедуры или функции.
  2. Управление возвращается в вызывающий код (вместе с возвращаемым значением, если это функция).
  3. Секция Исключение игнорируется, так как никакой ошибки в процессе выполнения не произошло.

Рассмотрим классический пример кода, где такое поведение оправдано:


Функция ПолучитьДанныеФайла(ПутьКФайлу)
    Попытка
        Файл = Новый ТекстовыйДокумент;
        Файл.Прочитать(ПутьКФайлу);
        
        Если Файл.КоличествоСтрок() = 0 Тогда
            // Файл пустой, продолжать алгоритм нет смысла
            Возврат Неопределено; 
        КонецЕсли;
        
        ТекстФайла = Файл.ПолучитьТекст();
        Возврат ТекстФайла;
    Исключение
        ЗаписатьЛогОшибки(ОписаниеОшибки());
        Возврат Неопределено;
    КонецПопытки;
КонецФункции

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

Важное отличие: 1С:Предприятие 8 против 1С:Шины и 1С:Элемента

Проектируя код, важно помнить о технологическом развитии экосистемы 1С. В классической платформе «1С:Предприятие 8» нет аналога конструкции finally (выполнение кода в любом случае, была ошибка или нет). Поэтому, если перед выходом из процедуры вам нужно обязательно закрыть соединение или освободить память, вам придется дублировать этот код перед каждым оператором Возврат.

Однако в современных решениях, таких как 1С:Шина и 1С:Предприятие.Элемент, встроенный язык был модернизирован. Там реализована полноценная конструкция Попытка — Поймать — В конце:


// Пример логики для сред нового поколения (1С:Элемент)
Попытка
    ОткрытьРесурс();
    Если Условие Тогда
        Возврат; // Выходим из метода
    КонецЕсли;
Поймать Исключение
    ОбработатьОшибку(Исключение);
В конце
    // Этот блок выполнится ОБЯЗАТЕЛЬНО, 
    // даже если внутри блока "Попытка" сработал "Возврат"
    ЗакрытьРесурс(); 
КонецПопытки;

В классической «1С:Предприятие 8» блока В конце (finally) нет, поэтому при использовании Возврат внутри Попытки вся ответственность за предварительную очистку ресурсов ложится на разработчика.

2. Вложенные попытки: правила работы и риск «замалчивания» ошибок

Платформа «1С:Предприятие» позволяет вкладывать блоки Попытка — Исключение друг в друга практически без ограничений по глубине. Это может происходить как внутри одного метода, так и при вызове процедур из разных модулей (например, когда метод общего модуля, обернутый в Попытку, вызывает метод модуля объекта, в котором также есть своя Попытка).

Несмотря на техническую поддержку со стороны платформы, здесь кроется серьезная архитектурная опасность — замалчивание (проглатывание) исключений.

Проанализируем ситуацию на примере:


// Модуль 1 (Вызывающий)
Процедура ВыполнитьОбменДанными()
    Попытка
        ВыгрузитьОбъектыВРеестр();
    Исключение
        // Этот блок никогда не выполнится, если во внутреннем коде 
        // ошибка была перехвачена и "заглушена"
        Сообщить("Произошла критическая ошибка обмена!");
    КонецПопытки;
КонецПроцедуры

// Модуль 2 (Общий модуль обработки данных)
Процедура ВыгрузитьОбъектыВРеестр() Экспорт
    Попытка
        // Код, вызывающий ошибку (например, деление на ноль или обращение к несуществующему файлу)
        Результат = 10 / 0; 
    Исключение
        // Ошибка перехвачена внутри, но никак не передана наружу!
        ЗаписьЖурналаРегистрации("Обмен", УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки());
    КонецПопытки;
КонецПроцедуры

В данном сценарии во внутреннем модуле произошел сбой. Однако, поскольку во вложенном блоке Исключение разработчик просто записал ошибку в журнал регистрации и завершил процедуру без вызова оператора ВызватьИсключение, вызывающий метод ВыполнитьОбменДанными() посчитает, что процедура отработала успешно. Бизнес-логика программы будет нарушена.

Правило разработки: Если вы перехватываете ошибку во внутреннем (вложенном) блоке, но она является критичной для общего алгоритма, вы обязаны либо передать признак ошибки через возвращаемое значение (например, вернуть Ложь вместо структуры данных), либо пробросить исключение наверх с помощью оператора ВызватьИсключение.

3. Смертельная опасность: Попытка внутри транзакции

Это самый важный нюанс, о котором часто забывают даже опытные разработчики. Использование конструкции Попытка — Исключение внутри активной транзакции базы данных может полностью заблокировать работу системы и привести к труднорасследуемым сбоям.

Рассмотрим, как работает транзакционный механизм в «1С:Предприятии»:

  1. Когда начинается транзакция (явно через НачатьТранзакцию() или неявно при записи/проведении документа), СУБД ожидает, что все операции внутри этой транзакции завершатся успешно.
  2. Если во время транзакции внутри блока Попытка происходит ошибка работы с базой данных (например, блокировка таблицы, нарушение уникальности индекса, ошибка СУБД при записи объекта), платформа 1С автоматически помечает текущую транзакцию как поврежденную (выставляется внутренний флаг запрета фиксации).
  3. Даже если вы успешно перехватили эту ошибку в блоке Исключение и решили продолжить выполнение кода (или просто сделать Возврат), транзакция уже физически не может быть зафиксирована.
  4. При любой последующей попытке записать что-либо в базу данных или при вызове ЗафиксироватьТранзакцию() система выбросит фатальную, неуправляемую ошибку: «В данной транзакции уже происходили ошибки!».

Посмотрим на классический пример некорректного кода:


// ТАК ДЕЛАТЬ НЕЛЬЗЯ!
НачатьТранзакцию();
Попытка
    ДокументОбъект = СсылкаНаДокумент.ПолучитьОбъект();
    ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
Исключение
    // Попытка "замять" ошибку записи и продолжить транзакцию
    Сообщить("Не удалось провести документ, но мы продолжим транзакцию...");
КонецПопытки;

// Следующая строка вызовет неуправляемое исключение платформы!
ЗаписатьСтатусВРегистрСведений(); 
ЗафиксироватьТранзакцию();

Правило стандартов разработки 1С: Нельзя использовать Попытка — Исключение внутри транзакции для того, чтобы проигнорировать ошибку записи в БД и продолжить выполнение текущей транзакции. Если запись объекта завершилась ошибкой, транзакция должна быть немедленно отменена с помощью оператора ОтменитьТранзакцию().

4. Современные стандарты «1С» по работе с исключениями

Чтобы ваш код был производительным, легко читаемым и не создавал проблем при обновлении конфигураций, придерживайтесь официальных стандартов разработки фирмы «1С»:

Давайте резюмируем. Оператор Возврат внутри блока Попытка использовать можно и нужно, если логика программы требует немедленного прекращения выполнения метода. Вложенность попыток также полностью поддерживается. Главное — помнить об опасности «замалчивания» ошибок, всегда контролировать состояние транзакций базы данных и делать блоки обработки исключений максимально узкими и локальными.

← На главную