Остановка кода во встроенном языке 1С 8.3: от выхода из процедуры до отмены фонового задания
Содержание
Чтобы прервать выполнение процедуры в 1С, напишите оператор Возврат; — управление сразу уходит к вызывающему коду. В функции то же самое, но с результатом: Возврат Неопределено;. Для выхода из цикла служит Прервать;, а чтобы оборвать всю цепочку вызовов разом — ВызватьИсключение.
Частое заблуждение — что Прервать останавливает процедуру. Нет: он завершает только ближайший цикл, а код после КонецЦикла выполнится как обычно. Ещё одна путаница — с долгими серверными вызовами: их не остановить ни оператором, ни кнопкой на клиенте. Ниже каждый случай со своим инструментом. Всё описанное относится к платформе 8.3 и работает в любой конфигурации — БП, ЗУП, УТ или собственной.
Выход из процедуры и функции оператором Возврат
Возврат — штатный способ завершить процедуру досрочно. Его ставят в начале метода как «охранное условие»: если данных нет, дальше идти незачем.
Процедура ЗаполнитьТабличнуюЧасть(Документ)
Если Не ЗначениеЗаполнено(Документ.Контрагент) Тогда
Сообщить("Не указан контрагент — заполнение отменено");
Возврат;
КонецЕсли;
// основная логика выполняется, только если контрагент указан
КонецПроцедуры
В функции Возврат обязан вернуть значение. Пустой Возврат; внутри функции синтаксическая проверка не пропустит.
Функция СуммаПоСтрокам(Таблица)
Если Таблица.Количество() = 0 Тогда
Возврат 0;
КонецЕсли;
Возврат Таблица.Итог("Сумма");
КонецФункции
Что полезно помнить про Возврат:
- 🔹 Он завершает только текущий метод. Если процедуру А вызвала процедура Б, то после
Возвратв А продолжится Б. - 🔹 Внутри
Попытка ... Исключениеоператор работает так же: выход из метода происходит сразу, блокИсключениене выполняется. - 🔹 Охранные условия в начале метода читаются легче, чем глубокая вложенность
Если ... Иначена три уровня.
Если вызывающий код должен узнать, что процедура вышла досрочно, превратите её в функцию с результатом Истина/Ложь. Тихий Возврат без сообщения потом трудно отлаживать: пользователь нажал кнопку, а ничего не произошло.
Прервать и Продолжить: как управлять циклами
Операторы Прервать и Продолжить действуют только внутри Для, Для Каждого и Пока.
| Оператор | Что делает | Типичный случай |
|---|---|---|
Прервать; | Выходит из ближайшего цикла, код после КонецЦикла выполняется | Нашли нужную строку, дальше искать незачем |
Продолжить; | Пропускает остаток текущего витка и переходит к следующему | Строку нужно пропустить по условию |
Возврат; | Выходит из цикла и из всего метода | Дальнейшая работа метода не имеет смысла |
Пример поиска первой строки с ошибкой:
НайденнаяСтрока = Неопределено;
Для Каждого Строка Из Товары Цикл
Если Строка.Количество = 0 Тогда
Продолжить; // пустые строки не проверяем
КонецЕсли;
Если Строка.Цена <= 0 Тогда
НайденнаяСтрока = Строка;
Прервать; // одна найденная строка — достаточно
КонецЕсли;
КонецЦикла;
Если НайденнаяСтрока <> Неопределено Тогда
Сообщить(СтрШаблон("Строка %1: не указана цена", НайденнаяСтрока.НомерСтроки));
КонецЕсли;
Выход из вложенных циклов
Во встроенном языке 1С нет оператора «прервать два цикла сразу» и нет меток. Прервать во внутреннем цикле вернёт вас во внешний. Рабочих приёмов три:
- Флаг. Во внутреннем цикле ставите
Найдено = ИстинаиПрервать, во внешнем сразу проверяете флаг и тоже вызываетеПрервать. - Вынос в функцию. Оба цикла переносите в отдельную функцию и выходите из неё через
Возврат— обычно это самый чистый вариант. - Цикл
Покас условием. Условие выхода ставите в заголовок:Пока Не Найдено И Сч < Всего Цикл.
Не удаляйте элементы коллекции внутри Для Каждого по ней же: итератор собьётся и часть строк будет пропущена. Для удаления используйте обратный цикл: Для Сч = 1 По Коллекция.Количество() Цикл Элемент = Коллекция[Коллекция.Количество() - Сч];.
Исключения: как прервать всю цепочку вызовов
Когда ошибка обнаружена глубоко — в общем модуле, вызванном из модуля объекта, вызванного из формы, — Возврат придётся протаскивать через каждый уровень. Исключение решает это одной строкой: ВызватьИсключение поднимает ошибку до ближайшего блока Попытка, а если его нет — до пользователя.
Процедура ПроверитьОстаток(Номенклатура, Количество) Экспорт
Остаток = ПолучитьОстаток(Номенклатура);
Если Остаток < Количество Тогда
ВызватьИсключение СтрШаблон("Недостаточно остатка: нужно %1, есть %2",
Формат(Количество, "ЧГ=0"), Формат(Остаток, "ЧГ=0"));
КонецЕсли;
КонецПроцедуры
Перехват на верхнем уровне:
Попытка
ПроверитьОстаток(Номенклатура, 10);
ОформитьОтгрузку();
Исключение
Инфо = ИнформацияОбОшибке();
ЗаписьЖурналаРегистрации("Отгрузка", УровеньЖурналаРегистрации.Ошибка, , ,
ПодробноеПредставлениеОшибки(Инфо));
Сообщить(КраткоеПредставлениеОшибки(Инфо));
КонецПопытки;
Правила, которые сберегут время:
- 🧱 Не глушите исключения пустым блоком
Исключение КонецПопытки. Ошибка исчезнет, а данные останутся в непонятном состоянии. - 🔁 Если блок
Исключениетолько пишет в журнал, а решать проблему должен вызывающий код, допишите в концеВызватьИсключение;без параметров — исходная ошибка пробросится дальше. - 🔒 В транзакции после исключения записывать уже нельзя: при следующем обращении к базе платформа выдаст
В данной транзакции уже происходили ошибки. Транзакцию нужно отменить (ОтменитьТранзакцию()) и начинать заново. - 📌
Попыткаставьте вокруг конкретной рискованной операции — записи, обращения к файлу, HTTP-запроса, а не вокруг всей процедуры на 300 строк.
Параметр Отказ в обработчиках событий
В обработчиках ПередЗаписью, ОбработкаПроверкиЗаполнения, ОбработкаПроведения, ПередУдалением есть параметр Отказ. Это самый правильный способ остановить запись или проведение: Отказ = Истина; плюс сообщение пользователю. Платформа сама отменит транзакцию записи.
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
Если Дата > ТекущаяДатаСеанса() + 86400 Тогда
Сообщить("Дата документа больше завтрашней — запись отменена");
Отказ = Истина;
Возврат;
КонецЕсли;
КонецПроцедуры
Обратите внимание на Возврат после Отказ = Истина: само присваивание процедуру не завершает, код ниже продолжит выполняться. Чтобы он не выполнялся впустую, выходите явно.
Остановка долгих процессов на клиенте и сервере
Где выполняется код, решает всё. Клиентский и серверный код прерываются разными средствами.
Ctrl+Break и ОбработкаПрерыванияПользователя
Если долгий цикл крутится на клиенте, пользователь может остановить его сочетанием Ctrl+Break — но только если в цикле есть вызов ОбработкаПрерыванияПользователя(). Платформа проверяет нажатие в момент вызова и прерывает выполнение ошибкой.
&НаКлиенте
Процедура ОбработатьФайлы(Команда)
Для Каждого ИмяФайла Из СписокФайлов Цикл
ОбработкаПрерыванияПользователя();
ОбработатьОдинФайл(ИмяФайла);
КонецЦикла;
КонецПроцедуры
Вызов дешёвый, но в цикле на миллион витков проверяйте нажатие, например, раз в 100 итераций.
Почему серверный вызов не остановить с клиента
Вызов &НаСервере из формы синхронный: клиент ждёт ответа, интерфейс замирает. Пока сервер не вернул управление, ни Ctrl+Break, ни закрытие формы выполнение не прервут. Если процедура считает 20 минут, пользователь будет смотреть на «песочные часы» все 20 минут.
Решение — перенести долгую работу в фоновое задание, а на клиенте опрашивать его состояние через ПодключитьОбработчикОжидания. Тогда форма остаётся живой, а пользователь получает кнопку «Отменить».
| Где выполняется | Чем прервать | Ограничение |
|---|---|---|
| Клиент, цикл | Ctrl+Break + ОбработкаПрерыванияПользователя() | Работает только там, где есть вызов |
| Сервер, синхронный вызов | Ничем со стороны клиента | Только завершение сеанса администратором |
| Фоновое задание | ФоновоеЗадание.Отменить() | Код задания должен быть готов к остановке |
| Регламентное задание | Отмена запущенного экземпляра, отключение расписания | Следующий запуск пройдёт по расписанию, если его не выключить |
Отмена фонового задания из кода и вручную
Фоновое задание запускают методом ФоновыеЗадания.Выполнить(). Он возвращает объект задания, а его УникальныйИдентификатор сохраняют в реквизит формы, чтобы потом найти задание и отменить.
&НаСервере
Процедура ЗапуститьРасчетНаСервере()
Параметры = Новый Массив;
Параметры.Добавить(Объект.Ссылка);
Задание = ФоновыеЗадания.Выполнить("РасчетыСервер.ДлительныйРасчет",
Параметры, , "Пересчёт себестоимости");
ИдентификаторЗадания = Задание.УникальныйИдентификатор;
КонецПроцедуры
&НаСервере
Процедура ОтменитьРасчетНаСервере()
Задание = ФоновыеЗадания.НайтиПоУникальномуИдентификатору(ИдентификаторЗадания);
Если Задание <> Неопределено
И Задание.Состояние = СостояниеФоновогоЗадания.Активно Тогда
Задание.Отменить();
КонецЕсли;
КонецПроцедуры
После отмены задание переходит в состояние СостояниеФоновогоЗадания.Отменено. Остальные значения: Активно, Завершено, ЗавершеноАварийно — последнее означает, что внутри задания упало исключение; текст ошибки лежит в свойстве ИнформацияОбОшибке.
Аккуратная остановка изнутри задания
Отмена обрывает выполнение в произвольной точке. Если задание обрабатывает 50 000 документов порциями, отмена посреди порции оставит часть данных записанной, часть нет. Поэтому длинные задания делят на шаги и пишут так, чтобы каждая порция шла в своей транзакции. Тогда после отмены база остаётся целостной, а повторный запуск продолжает с места остановки.
Типовые конфигурации на БСП оборачивают всё это в общий механизм длительных операций: ДлительныеОперации.ВыполнитьВФоне на сервере, ожидание с индикатором через ДлительныеОперацииКлиент.ОжидатьЗавершение и отмена через ДлительныеОперации.ОтменитьВыполнениеЗадания. В своей доработке типовой конфигурации лучше пользоваться ими, а не писать опрос состояния заново.
Отмена вручную администратором
В конфигурациях на БСП (БП 3.0, ЗУП 3.1, УТ 11) активные задания видны в разделе Администрирование → Обслуживание → Регламентные операции → Регламентные и фоновые задания. На вкладке фоновых заданий выберите активное и отмените его командой из командной панели.
Если задание не реагирует, а серверный вызов висит часами, остаётся консоль администрирования кластера: найдите информационную базу, откройте список сеансов и завершите сеанс, в котором идёт вызов.
Завершение сеанса обрывает работу пользователя без сохранения. Незаписанные данные в его формах пропадут, а начатая транзакция откатится. Предупредите пользователя заранее; если это массовая обработка, делайте это вне рабочего времени.
Как найти, где зависла процедура
Пользователь жалуется, что «всё висит», а код длинный. Прежде чем что-то прерывать, выясните, на какой строке он стоит.
Отладчик в Конфигураторе
- Запустите отладку из Конфигуратора или подключитесь к уже работающему сеансу через
Отладка → Подключение. - Когда процесс завис, выполните команду
Отладка → Прервать. Выполнение остановится на текущей строке, и вы увидите место в модуле. - Откройте стек вызовов и посмотрите, кто кого вызвал. Часто «висит» не цикл, а запрос внутри него.
- Пошагово пройдите код:
F10— шаг с обходом,F11— шаг с заходом в метод,F5— продолжить до следующей точки останова,F9— поставить или снять точку останова.
Для отладки серверного кода в клиент-серверном варианте сервер 1С должен быть запущен с разрешённой отладкой (ключ -debug у службы агента). Фоновые задания отлаживаются через автоподключение в том же окне Отладка → Подключение: включите там отладку фоновых заданий, и отладчик подхватит их при старте.
Замер производительности
Если процедура не висит, а просто медленная, включите Отладка → Замер производительности и выполните операцию. Замер покажет, сколько раз выполнилась каждая строка и сколько времени она заняла. Обычно 90 % времени съедают 2-3 строки — запрос в цикле или обращение к реквизиту через точку у ссылки.
Чего не делать, когда нужно остановить код
- ❌
ЗавершитьРаботуСистемы()для выхода из процедуры. Метод закрывает всё приложение пользователя. Это не способ прервать процедуру, это способ потерять его несохранённые данные. - ❌ Пустой
Возвратвместо исключения в общем модуле. Вызывающий код не узнает, что работа не выполнена, и продолжит, например, проводку документа с пустыми данными. - ❌
Попыткавокруг всего метода сСообщить(ОписаниеОшибки()). Пользователь видит текст, но в журнале регистрации следа нет, а транзакция может остаться в ошибочном состоянии. - ❌ Бесконечный
Пока Истина Циклбез предохранителя. Если условие выхода не сработает из-за неожиданных данных, процесс будет крутиться до завершения сеанса. Счётчик на 10 000 итераций с исключением стоит одной строки. - ❌
Отказ = Истинабез сообщения. Документ не записался, а пользователь не понимает почему и звонит вам.