Как Возврат завершает процедуры и функции во встроенном языке 1С 8.3
Содержание
Оператор Возврат (англ. Return) немедленно завершает текущую процедуру или функцию и передаёт управление в точку вызова. В функции после него указывают значение: Возврат Сумма;. В процедуре пишут просто Возврат;. Всё, что стоит ниже в той же ветке кода, уже не выполняется.
Частое заблуждение новичков — считать, что возврат в 1С
выходит только из цикла, как Прервать. Это не так: Возврат прекращает работу всей процедуры целиком, сколько бы циклов и условий ни было вложено. Поведение одинаково для всех конфигураций, потому что это механизм платформы, а не БП или ЗУП.
Синтаксис Возврат и где его можно писать
Оператор имеет две формы:
Возврат; // в процедуре — просто выход
Возврат Выражение; // в функции — выход с результатом
Выражением может быть что угодно: число, строка, ссылка, переменная, вызов другой функции, тернарный оператор ?(...).
Функция ЦенаСНДС(Цена, СтавкаПроцент)
Возврат Цена + Цена * СтавкаПроцент / 100;
КонецФункции
Вызов ЦенаСНДС(1000, 20) вернёт 1200.
Правила размещения:
- 📌
Возвратпишут только внутри телаПроцедура … КонецПроцедурыилиФункция … КонецФункции. - 📌 В одной процедуре операторов может быть сколько угодно — по одному на каждую ветку выхода.
- 📌 После
Возвратставится точка с запятой, как после любого оператора. - 📌 Регистр не важен:
ВОЗВРАТ,Возвративозвратдля компилятора одно и то же.
В конфигураторе и EDT ключевое слово подсвечивается как зарезервированное. Если слово не окрасилось, проверьте раскладку: вместо кириллической «В» могла попасть латинская «B».
Чем возврат из процедуры отличается от возврата из функции
Разница в том, обязано ли что-то вернуться в точку вызова.
| Процедура | Функция | |
|---|---|---|
| Форма оператора | Возврат; | Возврат Значение; |
Вызов в выражении Х = Имя() | недопустим | основной способ |
Вызов отдельной строкой Имя(); | основной способ | допустим, результат теряется |
Если Возврат не встретился | процедура просто дошла до конца | функция вернула Неопределено |
Функция без Возврат на каком-то пути
Если выполнение дошло до КонецФункции, не встретив Возврат, функция вернёт Неопределено. Ошибки при этом нет, и это коварно.
Функция СкидкаКлиента(Клиент)
Если Клиент.Постоянный Тогда
Возврат 5;
КонецЕсли;
// забыли вернуть значение для остальных клиентов
КонецФункции
Для обычного клиента вызов вернёт Неопределено. Выражение Сумма * СкидкаКлиента(Клиент) / 100 упадёт с ошибкой преобразования типа, причём не в этой функции, а там, где результат использовали. Ищут такое долго.
Правильный вариант — явный возврат на каждом пути:
Функция СкидкаКлиента(Клиент)
Если Клиент.Постоянный Тогда
Возврат 5;
КонецЕсли;
Возврат 0;
КонецФункции
Процедуру нельзя использовать как функцию
Строка Результат = ЗаполнитьТаблицу();, где ЗаполнитьТаблицу — процедура, приводит к ошибке «Использование процедуры как функции». Обратный случай допустим: функцию можно вызвать отдельной строкой, её результат просто отбросится.
Как вернуть несколько значений
Возврат отдаёт одно значение. Когда нужно несколько, собирают их в структуру:
Функция ИтогиПоДокументу(Документ)
Итоги = Новый Структура("Сумма, НДС, Строк", 0, 0, 0);
Для Каждого Строка Из Документ.Товары Цикл
Итоги.Сумма = Итоги.Сумма + Строка.Сумма;
Итоги.НДС = Итоги.НДС + Строка.СуммаНДС;
Итоги.Строк = Итоги.Строк + 1;
КонецЦикла;
Возврат Итоги;
КонецФункции
Вызывающий код читает Итоги.Сумма, Итоги.НДС и Итоги.Строк. Второй способ — менять параметры, переданные по ссылке (без Знач), но для читаемости структура лучше: по вызову сразу видно, что функция что-то возвращает.
Возврат внутри циклов и вложенных условий
В циклах у программиста три инструмента, и путать их нельзя.
| Оператор | Что прерывает | Куда переходит управление |
|---|---|---|
Продолжить | текущую итерацию | на следующий проход того же цикла |
Прервать | только ближайший цикл | на строку после КонецЦикла |
Возврат | всю процедуру или функцию | в точку вызова |
Поиск с выходом из вложенных циклов
В 1С нет меток и нельзя выйти из двух циклов одним Прервать. Возврат решает задачу сразу: как только нашли нужное, выходим.
Функция НайтиЯчейку(Матрица, Искомое)
Для НомерСтроки = 0 По Матрица.ВГраница() Цикл
Строка = Матрица[НомерСтроки];
Для НомерКолонки = 0 По Строка.ВГраница() Цикл
Если Строка[НомерКолонки] = Искомое Тогда
Возврат Новый Структура("Строка, Колонка", НомерСтроки, НомерКолонки);
КонецЕсли;
КонецЦикла;
КонецЦикла;
Возврат Неопределено;
КонецФункции
Для матрицы из 100×100 элементов, где значение лежит в третьей строке, функция проверит не больше трёхсот ячеек, а не все десять тысяч.
Если вынести поиск в отдельную функцию нельзя, во вложенных циклах заводят флаг Найдено и проверяют его после внутреннего цикла с Прервать. Это работает, но код длиннее и легче ошибиться.
Ранний выход вместо глубокой вложенности
Приём называют «сторожевыми условиями» (guard clauses). Проверки, при которых делать нечего, ставят в начало и сразу выходят:
Процедура ПересчитатьСуммы(Объект)
Если Объект.Товары.Количество() = 0 Тогда
Возврат;
КонецЕсли;
Если НЕ ЗначениеЗаполнено(Объект.Контрагент) Тогда
Возврат;
КонецЕсли;
// основная логика без лишних уровней отступа
Для Каждого Строка Из Объект.Товары Цикл
Строка.Сумма = Строка.Цена * Строка.Количество;
КонецЦикла;
КонецПроцедуры
Без ранних выходов основная логика сидела бы внутри двух вложенных Если. При пяти-шести проверках код уезжает вправо, и читать его становится тяжело.
Код после Возврат: недостижимые участки и как их найти
Всё, что стоит после Возврат в той же ветке, никогда не выполнится. Компилятор 1С такой код не запрещает и предупреждения не выдаёт.
Функция ОстатокТовара(Номенклатура)
Результат = ПолучитьОстаток(Номенклатура);
Возврат Результат;
ЗаписатьЖурналРегистрации("Остатки", УровеньЖурналаРегистрации.Информация, , , "Запрошен остаток");
КонецФункции
Запись в журнал регистрации здесь мёртвая. Обычно такое появляется, когда в функцию дописали строки «в конец», не заметив выхода выше.
Чем проверить модуль
- 🔍 Синтаксический контроль конфигуратора (
Ctrl+F7в модуле) ловит грубые ошибки:Возвратсо значением в процедуре или выход вне тела метода. Недостижимый код он не показывает. - 🔍 1С:EDT и подключённый к нему или к VS Code BSL Language Server подсвечивают недостижимые строки и функции, у которых есть путь без возврата значения.
- 🔍 SonarQube с плагином для 1С показывает те же замечания по всей конфигурации — удобно для командной разработки.
- 🔍 Отладчик: поставьте точку останова на подозрительную строку. Если она ни разу не срабатывает при заведомо проходящем сценарии, ищите выход выше.
Возврат «для отладки» — забытая ловушка
Разработчик временно ставит Возврат; в начало процедуры, чтобы отключить её, и забывает убрать. Обработка перестаёт что-либо делать, ошибок нет. Перед выкладкой изменений ищите по модулю строку Возврат; с комментарием вроде «временно» или «отладка» через Ctrl+F.
Возврат в блоке Попытка…Исключение и в транзакциях
Возврат можно писать и внутри Попытка, и внутри Исключение. Выход происходит сразу, как обычно. Но во встроенном языке 1С нет блока, который выполнится в любом случае (аналога finally). Значит, код после КонецПопытки при выходе из середины попытки пропускается.
Опасный пример с транзакцией
// ПЛОХО
НачатьТранзакцию();
Попытка
Если НЕ ПроверитьОстатки(Документ) Тогда
Возврат; // транзакция осталась открытой
КонецЕсли;
ЗаписатьДвижения(Документ);
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ВызватьИсключение;
КонецПопытки;
При выходе по условию никто не вызвал ни ЗафиксироватьТранзакцию(), ни ОтменитьТранзакцию(). Транзакция висит до конца серверного вызова, а дальше платформа её откатит — и откатит заодно всё, что успели записать в той же транзакции выше по стеку. Внешний код получит ошибку уже в другом месте.
Не выходите оператором Возврат из открытой транзакции. Перед выходом закройте её явно:
// ХОРОШО
НачатьТранзакцию();
Попытка
Если НЕ ПроверитьОстатки(Документ) Тогда
ОтменитьТранзакцию();
Возврат;
КонецЕсли;
ЗаписатьДвижения(Документ);
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
ВызватьИсключение;
КонецПопытки;
Ещё лучше — вынести проверку до НачатьТранзакцию(), тогда открывать транзакцию вообще не придётся.
Возврат или ВызватьИсключение
| Ситуация | Что использовать |
|---|---|
| Делать нечего, и это нормально (пустая табличная часть) | Возврат; |
| Функция не нашла значение, и вызывающий это ожидает | Возврат Неопределено; |
| Операция невозможна, вызывающий должен узнать причину | ВызватьИсключение "Текст причины"; |
| Ошибку поймали, записали в журнал и хотим пробросить дальше | ВызватьИсключение; в блоке Исключение |
Если в блоке Исключение поставить Возврат и ничего не записать в журнал регистрации, ошибка исчезнет бесследно. Пользователь увидит, что «ничего не произошло», а причину не найдёт никто.
Возврат в обработчиках событий и серверных вызовах
Здесь несколько практических тонкостей, которые не видно из синтаксиса.
Возврат не отменяет запись документа
В обработчиках ПередЗаписью, ОбработкаПроведения, ОбработкаПроверкиЗаполнения выход через Возврат; всего лишь завершает обработчик. Запись или проведение продолжится. Чтобы остановить операцию, выставляют параметр Отказ:
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
Если Товары.Количество() = 0 Тогда
ОбщегоНазначения.СообщитьПользователю("Заполните табличную часть «Товары».");
Отказ = Истина;
Возврат;
КонецЕсли;
// остальная логика
КонецПроцедуры
ОбщегоНазначения.СообщитьПользователю есть в конфигурациях на БСП (БП 3.0, ЗУП 3.1, УТ 11). В самописной конфигурации без БСП используйте объект СообщениеПользователю.
Что можно вернуть с сервера на клиент
Функция с директивой &НаСервере, которую вызывают из &НаКлиенте, может вернуть только значения, передаваемые между клиентом и сервером:
- ✅ примитивы: число, строка, дата, булево,
Неопределено; - ✅ ссылки на объекты базы;
- ✅
Структура,Массив,Соответствие, их фиксированные варианты; - ❌
ТаблицаЗначений,Запрос,РезультатЗапроса, объекты справочников и документов.
Если нужна таблица, на сервере её превращают в массив структур или помещают во временное хранилище (ПоместитьВоВременноеХранилище) и возвращают адрес.
Возврат в асинхронных функциях
В платформе 8.3 начиная с версии 8.3.18 есть Асинх Функция. Возврат Значение; внутри неё не отдаёт значение напрямую: вызывающий получает Обещание, а само значение — через Ждать.
&НаКлиенте
Асинх Функция ПодтвердитьУдаление()
Ответ = Ждать ВопросАсинх("Удалить строку?", РежимДиалогаВопрос.ДаНет);
Возврат Ответ = КодВозвратаДиалога.Да;
КонецФункции
&НаКлиенте
Асинх Процедура УдалитьСтроку(Команда)
Если НЕ Ждать ПодтвердитьУдаление() Тогда
Возврат;
КонецЕсли;
// удаление
КонецПроцедуры
Без Ждать условие Если НЕ ПодтвердитьУдаление() сравнит объект Обещание, а не булево, и логика сломается.
Ошибки с оператором возврата в 1С и правила чистого кода
Правила, которые экономят время при сопровождении
- Один тип результата. Функция возвращает либо число, либо
Неопределенокак признак «не найдено». Не смешивайте числа, строки и булево из одной функции — вызывающему придётся проверять тип. - Явный
Возвратв последней строке функции. Даже если кажется, что все пути закрыты условиями выше. - Ранние выходы — в начале метода.
Возвратв середине длинного цикла на 80 строк читается плохо: его легко не заметить при доработке. - Не больше одного уровня «хитрости». Если в функции пять
Возвратв разных местах тела, проще разбить её на две-три функции поменьше. - Вычисление — до
Возврат, а не внутри.Возврат Результат;удобнее отлаживать, чемВозврат СложноеВыражение(...): значение видно в отладчике до выхода.