Остановка выполнения кода в 1С 8.3: от Ctrl+Break до Возврат и исключений

· 9 мин

Содержание

Прервать функцию в 1С можно тремя путями. Из кода — оператором Возврат (выход из функции), Прервать (выход из цикла) или ВызватьИсключение (аварийная остановка всей цепочки вызовов). Снаружи, когда функция зависла в режиме 1С:Предприятие, — сочетанием Ctrl+Break, а если не помогает, завершением сеанса или процесса клиента.

В отладке выполнение останавливают командой Прервать в меню Отладка конфигуратора: отладчик встаёт на строку, которая выполняется прямо сейчас. Так быстрее всего найти бесконечный цикл.

Какой способ остановки выбрать

Способ зависит от того, кто прерывает: разработчик в коде, пользователь в работающей программе или администратор на сервере.

СитуацияЧем прерватьЧто потеряется
Функция нашла результат раньше концаВозврат Значение;Ничего, штатный выход
Нужно выйти из цикла, но не из функцииПрервать;Ничего
Пропустить одну итерациюПродолжить;Ничего
Данные некорректны, дальше считать нельзяВызватьИсключение "Текст";Незафиксированная транзакция откатится
Долгий цикл на клиенте, пользователь ждётCtrl+Break + ОбработкаПрерыванияПользователя() в кодеРезультат текущей операции
Завис серверный вызовЗавершение сеанса в консоли кластераНесохранённые данные пользователя
Долгая операция в фонеФоновоеЗадание.Отменить()Результат задания
Отладка, непонятно где виситОтладка → ПрерватьНичего, выполнение на паузе

Если операцию приходится регулярно прерывать вручную, это сигнал перенести её в фоновое задание. Тогда отмена станет штатной кнопкой, а не снятием процесса.

Как прервать зависшую функцию в режиме 1С:Предприятие

Сочетание Ctrl+Break

Первое, что стоит попробовать, — Ctrl+Break. Платформа прерывает выполняемый на клиенте код и сообщает, что действие прервано пользователем.

Ограничения у этого способа серьёзные:

  • 🔹 срабатывает в клиентском коде, когда в цикле есть вызов ОбработкаПрерыванияПользователя() или когда платформа сама проверяет прерывание;
  • 🔹 если клиент ждёт ответа от сервера (запрос, проведение, запись), сочетание клавиш серверный вызов обычно не останавливает;
  • 🔹 на ноутбуках без клавиши Break её заменяет комбинация с Fn — какая именно, зависит от модели.

Завершение процесса клиента

Если программа не отвечает несколько минут, а Ctrl+Break не помог, закройте процесс в диспетчере задач Windows:

  1. Нажмите Ctrl+Shift+Esc.
  2. Найдите процесс 1cv8c.exe (тонкий клиент) или 1cv8.exe (толстый клиент, конфигуратор).
  3. Выберите «Снять задачу».

Всё, что пользователь не записал, пропадёт. В файловой базе обрыв в момент записи иногда оставляет повреждённые данные, поэтому после аварийного закрытия стоит запустить тестирование и исправление в конфигураторе.

Завершение сеанса на сервере 1С

В клиент-серверной базе код может продолжать работать на сервере даже после закрытия окна клиента. Тогда сеанс завершают в консоли кластера:

  1. Откройте оснастку «Администрирование серверов 1С:Предприятия».
  2. Раскройте кластер → Информационные базы → нужная база → Сеансы.
  3. Найдите сеанс по имени пользователя и времени начала.
  4. Удалите сеанс через контекстное меню.

Из командной строки то же делает утилита rac:

rac cluster list
rac session list --cluster=<uuid кластера>
rac session terminate --cluster=<uuid кластера> --session=<uuid сеанса>

Завершение сеанса обрывает работу пользователя без предупреждения. Транзакция, которую выполнял сеанс, откатится. Предупредите человека заранее и не завершайте сеанс, который проводит закрытие месяца или загрузку обмена, без согласования.

Выход из функции оператором Возврат

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

Типичный приём — ранний выход по условию:

Функция НайтиСтрокуПоАртикулу(Таблица, Артикул)
    
    Если Не ЗначениеЗаполнено(Артикул) Тогда
        Возврат Неопределено;
    КонецЕсли;
    
    Для Каждого Строка Из Таблица Цикл
        Если Строка.Артикул = Артикул Тогда
            Возврат Строка;
        КонецЕсли;
    КонецЦикла;
    
    Возврат Неопределено;
    
КонецФункции

Здесь Возврат внутри цикла сразу прекращает и цикл, и функцию. Отдельный Прервать не нужен.

Что учитывать при раннем выходе:

  • 📌 Все ветки функции должны возвращать значение одного смысла. Если одна ветка возвращает строку таблицы, а другая Ложь, вызывающий код начнёт ошибаться.
  • 📌 Возврат между НачатьТранзакцию() и ЗафиксироватьТранзакцию() оставит транзакцию открытой: изменения не зафиксируются, а последующий код получит ошибку.
  • 📌 Возврат прерывает только текущий вызов. Если функцию вызвала другая функция, та продолжит работу со следующей строки.

Ранний выход в начале функции («проверили — вышли») читается легче, чем вложенные Если на три уровня. Это обычная практика в коде типовых конфигураций.

Как прервать цикл и перейти к следующей итерации

Оператор Прервать

Прервать завершает ближайший цикл Для, Для Каждого или Пока и передаёт управление на строку после КонецЦикла. Функция при этом продолжает работать.

Оператор Продолжить

Продолжить пропускает остаток текущей итерации и сразу переходит к следующей:

Для Каждого Строка Из Объект.Товары Цикл
    Если Строка.Количество <= 0 Тогда
        Продолжить;
    КонецЕсли;
    Строка.Сумма = Строка.Количество * Строка.Цена;
КонецЦикла;

Строки с нулевым количеством останутся без пересчёта, остальные получат сумму.

Выход из вложенных циклов

Главная ловушка: Прервать выходит только из одного, внутреннего цикла. Внешний продолжит крутиться. Есть два рабочих решения.

Первое — флаг:

Найдено = Ложь;
Для Каждого Документ Из СписокДокументов Цикл
    Для Каждого Строка Из Документ.Товары Цикл
        Если Строка.Количество = 0 Тогда
            Найдено = Истина;
            Прервать;
        КонецЕсли;
    КонецЦикла;
    Если Найдено Тогда
        Прервать;
    КонецЕсли;
КонецЦикла;

Второе — вынести оба цикла в отдельную функцию и выйти через Возврат. Код получается короче, и намерение видно сразу.

В языке есть и оператор Перейти ~Метка;, но им почти не пользуются: код с переходами трудно сопровождать, а прыгнуть внутрь цикла нельзя.

ОператорЧто прерываетКуда переходит управление
ПродолжитьТекущую итерациюК началу следующей итерации
ПрерватьБлижайший циклНа строку после КонецЦикла
ВозвратПроцедуру или функциюВ вызывающий код
ВызватьИсключениеВсю цепочку вызовов до ближайшей ПопыткаВ блок Исключение или к сообщению об ошибке

Аварийный выход через исключение

Когда продолжать бессмысленно (не заполнена организация, нет курса валюты, нарушена целостность данных), функцию останавливают исключением:

Если Не ЗначениеЗаполнено(Организация) Тогда
    ВызватьИсключение "Не указана организация, расчёт остановлен";
КонецЕсли;

В отличие от Возврат, исключение поднимается по стеку вызовов вверх, пока не встретит блок Попытка ... Исключение. Если такого блока нет, пользователь увидит окно с текстом ошибки, а операция прекратится целиком.

Перехват и продолжение работы

Чтобы ошибка в одном документе не останавливала обработку всего списка, оберните проблемный участок в Попытка:

Процедура ПровестиПакет(МассивДокументов)
    Для Каждого Ссылка Из МассивДокументов Цикл
        Попытка
            Объект = Ссылка.ПолучитьОбъект();
            Объект.Записать(РежимЗаписиДокумента.Проведение);
        Исключение
            ТекстОшибки = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
            ЗаписьЖурналаРегистрации("ПакетноеПроведение",
                УровеньЖурналаРегистрации.Ошибка, , Ссылка, ТекстОшибки);
        КонецПопытки;
    КонецЦикла;
КонецПроцедуры

Ошибочные документы попадут в журнал регистрации, остальные проведутся.

Исключение внутри транзакции

С транзакциями правило жёсткое: поймали исключение — отмените транзакцию и пробросьте ошибку дальше.

НачатьТранзакцию();
Попытка
    // запись данных
    ЗафиксироватьТранзакцию();
Исключение
    ОтменитьТранзакцию();
    ВызватьИсключение;
КонецПопытки;

ВызватьИсключение без параметров внутри блока Исключение повторно выбрасывает ту же ошибку. Если её «проглотить» и продолжить запись, платформа выдаст В данной транзакции уже происходили ошибки: после сбоя транзакцию можно только отменить.

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

Как прервать выполнение в отладчике

В режиме отладки остановить код проще всего: выполнение ставится на паузу, и видно, где именно оно находится.

Пауза на текущей строке

  1. Запустите отладку из конфигуратора или подключитесь к уже запущенному клиенту через Отладка → Подключение.
  2. Когда программа «задумалась», выберите в конфигураторе Отладка → Прервать.
  3. Отладчик откроет модуль на строке, которая выполняется в этот момент.
  4. Откройте Отладка → Стек вызовов, чтобы увидеть цепочку процедур, которая привела к этой строке.

Если вы попали внутрь цикла, посмотрите значения переменных в табло или через вычисление выражения. Счётчик в несколько миллионов или переменная, которая не меняется между итерациями, сразу показывают причину зависания.

Как выйти из отладки

  • ▶️ Продолжить отладку — код выполняется дальше до следующей точки останова.
  • ⏹ Завершить отладку — сеанс отладки закрывается вместе с запущенным клиентом.
  • 🔌 Отключение отладчика от предметов отладки — клиент продолжает работать сам по себе.

Для серверного кода в клиент-серверной базе отладку на сервере нужно разрешить заранее: служба агента сервера запускается с ключом -debug. Без этого Прервать остановит только клиентскую часть.

Включите Отладка → Остановка по ошибке. Отладчик будет вставать прямо на строке, где возникло исключение, даже если его потом перехватывает Попытка.

Защита от бесконечных циклов и зависаний

Лучше, чтобы функцию не приходилось прерывать снаружи. Для этого в долгих участках закладывают точки выхода.

Ограничитель итераций

Цикл Пока с условием, которое может никогда не стать ложным, ограничьте счётчиком. Пример — подъём по иерархии справочника, где из-за ошибки в данных родитель может ссылаться на потомка:

Счетчик = 0;
ТекущийЭлемент = Элемент;
Пока ЗначениеЗаполнено(ТекущийЭлемент) Цикл
    Счетчик = Счетчик + 1;
    Если Счетчик > 1000 Тогда
        ВызватьИсключение "Слишком глубокая иерархия: вероятна циклическая ссылка";
    КонецЕсли;
    ТекущийЭлемент = ТекущийЭлемент.Родитель;
КонецЦикла;

Проверка Ctrl+Break в клиентском цикле

Чтобы пользователь мог остановить долгую обработку на клиенте, вызывайте в цикле ОбработкаПрерыванияПользователя():

&НаКлиенте
Процедура ОбработатьСтроки(Команда)
    Для Каждого Строка Из Объект.Товары Цикл
        ОбработкаПрерыванияПользователя();
        // обработка строки
    КонецЦикла;
КонецПроцедуры

Процедура проверяет, нажал ли пользователь Ctrl+Break, и если да — прерывает выполнение.

Фоновое задание с отменой

Долгие серверные операции (загрузка, массовое перепроведение, расчёт) выносите в фоновое задание. Отменить его можно программно:

Задание = ФоновыеЗадания.НайтиПоУникальномуИдентификатору(ИдентификаторЗадания);
Если Задание <> Неопределено
    И Задание.Состояние = СостояниеФоновогоЗадания.Активно Тогда
    Задание.Отменить();
КонецЕсли;

Отмена срабатывает не мгновенно, поэтому интерфейс должен показывать статус «отменяется», а не сразу «готово».

Ещё несколько точек защиты:

  • ⏱ у HTTPСоединение задавайте таймаут в конструкторе — иначе зависший внешний сервис подвесит и вашу функцию;
  • 🔁 рекурсию ограничивайте параметром глубины, как цикл — счётчиком;
  • 📦 большие выборки обрабатывайте порциями, а не одним запросом на миллион строк.

Диагностика: почему функция зависает

Прерывание снимает симптом. Чтобы зависание не повторилось, найдите причину.

Диагностика: выберите симптом

Инструменты для поиска причины:

  1. Замер производительности (Отладка → Замер производительности в конфигураторе) показывает, сколько времени и сколько раз выполнялась каждая строка. Строка с миллионом вызовов — кандидат номер один.
  2. Технологический журнал настраивается файлом logcfg.xml и пишет длительные вызовы, запросы к СУБД и ожидания на блокировках. Подходит, когда зависание воспроизводится только у пользователей.
  3. Журнал регистрации показывает, на каком событии сеанс остановился и какие ошибки были до этого.

Частые вопросы

Чем Возврат отличается от Прервать?
Прервать выходит только из цикла, функция продолжает работу. Возврат завершает всю процедуру или функцию.
Можно ли выйти сразу из двух вложенных циклов одной командой?
Нет. Прервать действует на ближайший цикл. Используйте флаг или вынесите циклы в функцию с Возврат.
Почему Ctrl+Break не останавливает проведение документа?
Проведение выполняется на сервере, а сочетание клавиш обрабатывается на клиенте. Остановить такой вызов можно завершением сеанса в консоли кластера.
Что будет с данными, если завершить зависший сеанс?
Незафиксированная транзакция откатится, уже записанные до неё данные останутся. Несохранённые формы пользователя пропадут.
Как остановить код, если отладчик не встаёт на паузу?
Проверьте, что отладка разрешена на сервере и отладчик подключён к нужному предмету отладки: клиенту или серверу.