Разработка и Конфигурация · 11 мин
Как работает цикл в 1С: синтаксис, примеры и типичные ошибки
Как работает цикл в 1С: синтаксис Для, Пока, Для каждого, операторы Прервать и Продолжить, обход таблиц и выборок, типичные ошибки и бесконечные циклы.
Цикл в языке 1С — это конструкция, которая многократно выполняет один и тот же блок кода, пока выполняется заданное условие или пока не закончатся элементы коллекции. Без циклов невозможно обойти строки табличной части документа, обработать выборку из запроса или посчитать итоги по массиву, поэтому это одна из первых тем, которые осваивает разработчик 1С.
Во встроенном языке 1С существует три вида циклов: Для … Цикл (счётчик), Для каждого … Из … Цикл (обход коллекции) и Пока … Цикл (цикл с условием). Дополнительно есть два управляющих оператора: Прервать досрочно завершает цикл, а Продолжить переходит к следующей итерации, пропуская остаток тела цикла.
Выбор конкретного вида цикла зависит от задачи: если известно число повторений — удобнее счётчик, если нужно обойти коллекцию — Для каждого, если повторения зависят от вычисляемого условия — Пока. Ниже разобран синтаксис каждого варианта, практические сценарии и ошибки, которые чаще всего приводят к зависанию кода или неверным результатам.
Как устроен цикл «Для» со счётчиком
Цикл Для используется, когда заранее известно, сколько раз нужно повторить действие. Переменная-счётчик автоматически увеличивается на единицу при каждой итерации — вручную изменять её не требуется. Синтаксис выглядит так:
Для Счетчик = 1 По 10 Цикл
Сообщить("Итерация номер " + Счетчик);
КонецЦикла;
Здесь переменная Счетчик последовательно принимает значения от 1 до 10 включительно, и тело цикла выполнится десять раз. Границы могут быть любыми числовыми выражениями, в том числе переменными и вызовами функций. Если начальное значение больше конечного, тело цикла не выполнится ни разу — это нормальное поведение, а не ошибка.
Обратите внимание: шаг счётчика всегда равен единице, отрицательный шаг не поддерживается. Если нужен обратный порядок, проще вычислять индекс внутри тела цикла, например Индекс = ВерхняяГраница - Счетчик + 1, либо использовать обход коллекции с конца через индексы.
Обход коллекций через «Для каждого»
Цикл Для каждого — основной инструмент для перебора массивов, таблиц значений, табличных частей, структур и строк выборки. На каждой итерации переменная цикла получает ссылку на очередной элемент коллекции:
Для каждого СтрокаТовара Из Документ.Товары Цикл
СтрокаТовара.Сумма = СтрокаТовара.Цена * СтрокаТовара.Количество;
КонецЦикла;
Такой вариант короче и безопаснее обхода по индексам: не нужно следить за границами коллекции, и невозможно выйти за её пределы. Изменения, внесённые в элемент через переменную цикла (как в примере с пересчётом суммы), сохраняются в самой коллекции, потому что переменная ссылается на тот же объект.
Важное ограничение: внутри цикла Для каждого нельзя добавлять или удалять элементы той коллекции, которую обходите. Это приводит к непредсказуемому поведению или ошибке. Если нужно удалить часть строк, обходите коллекцию по индексам с конца к началу либо сначала соберите элементы для удаления в отдельный массив.
Цикл «Пока» и работа с выборками запросов
Цикл Пока выполняется, пока условие истинно. Условие проверяется перед каждой итерацией, поэтому если оно ложно с самого начала, тело не выполнится ни разу. Классический сценарий — обход выборки результата запроса:
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Сообщить(Выборка.Номенклатура);
КонецЦикла;
Метод Следующий() возвращает Истина, пока в выборке есть необработанные строки, и Ложь, когда они закончились. Это безопасная конструкция: цикл гарантированно завершится, когда данные исчерпаются.
Опасность возникает, когда условие зависит от переменных, которые программист должен изменять вручную внутри тела. Если забыть увеличить счётчик или обновить признак, условие никогда не станет ложным — получится бесконечный цикл, и сеанс 1С «зависнет». Перед запуском такого кода убедитесь, что на каждой ветке выполнения тела цикла условие рано или поздно изменится.
Управление выполнением: Прервать и Продолжить
Оператор Прервать немедленно завершает цикл, даже если условие ещё позволяет продолжать. Это удобно при поиске: как только нужный элемент найден, дальнейший перебор бессмыслен и только тратит время:
Для каждого Строка Из ТаблицаЗначений Цикл
Если Строка.Артикул = ИскомыйАртикул Тогда
НайденнаяСтрока = Строка;
Прервать;
КонецЕсли;
КонецЦикла;
Оператор Продолжить пропускает оставшуюся часть тела цикла и сразу переходит к следующей итерации. Он помогает избежать глубокой вложенности условий: вместо того чтобы оборачивать весь код в Если … Тогда, в начале итерации отсекают неподходящие элементы.
Оба оператора действуют только на тот цикл, в котором находятся непосредственно. Если циклы вложены друг в друга и нужно прервать внешний цикл из внутреннего, используют переменную-флаг: внутренний цикл устанавливает её и прерывается, а внешний проверяет флаг и тоже выполняет Прервать.
Типичные сценарии: вложенные циклы и обход по индексам
Вложенные циклы применяют, когда каждый элемент одной коллекции нужно сопоставить с элементами другой — например, при проверке дублей или построении таблицы соответствий. Помните, что время выполнения перемножается: два цикла по тысяче элементов дают миллион итераций. На больших объёмах такой подход заметно тормозит, и лучше использовать запрос с соединением таблиц либо соответствие для быстрого поиска.
Обход коллекции по индексам нужен, когда важна позиция элемента или требуется удаление строк. Индексация в коллекциях 1С начинается с нуля:
Для Индекс = 0 По Таблица.Количество() - 1 Цикл
Строка = Таблица[Индекс];
// обработка строки
КонецЦикла;
Частая ошибка здесь — использовать границу Таблица.Количество() без вычитания единицы, что приводит к обращению за пределы коллекции и падению кода с ошибкой. Второй промах — вызывать Количество() на каждой итерации в условии Пока: для больших коллекций лучше один раз сохранить значение в переменную.
Производительность: когда цикл — не лучшее решение
Любая обработка данных в цикле на встроенном языке медленнее, чем аналогичная операция на уровне запроса. Если задача — выбрать, сгруппировать или соединить данные из базы, правильнее написать запрос, чем выгружать всё в таблицу значений и перебирать строки. Запрос выполняется на сервере СУБД и работает на порядки быстрее.
Внутри цикла следует избегать обращений к базе данных: запрос или получение объекта в каждой итерации превращает один обращение в сотни. Правильный приём — вынести выборку за пределы цикла, получить все нужные данные одним запросом и внутри цикла только искать по ним через соответствие или индексированную таблицу значений.
Для проверки скорости участка кода используйте замер производительности в конфигураторе (Отладка → Замер производительности). Он покажет, сколько времени заняла каждая строка, и поможет найти «тяжёлые» итерации, например скрытые вызовы запросов внутри тела цикла.
Как выбрать действие
Как проверить, что цикл работает правильно
Самый надёжный способ — отладчик конфигуратора. Поставьте точку останова на строке Цикл и внутри тела, запустите отладку и наблюдайте, как меняются переменные на каждой итерации через табло (Shift+F9). Так видно и количество итераций, и корректность изменения данных.
Для быстрой проверки без отладчика добавьте временный счётчик итераций и выведите его после цикла через Сообщить(). Если число не совпадает с ожидаемым размером коллекции — часть элементов пропускается или цикл завершается раньше времени. После проверки отладочные сообщения нужно удалить, чтобы не засорять журнал пользователей.
Признаки корректной работы: цикл завершается за предсказуемое время, обрабатывает все элементы ровно один раз и не изменяет коллекции, которые не должен менять. Если обработка «зависает», первым делом проверьте условие цикла Пока и логику операторов Прервать/Продолжить — именно там чаще всего скрывается причина.
Как проверить результат
Совпадает с размером коллекции или заданным диапазоном
Меньше ожидаемого — элементы пропускаются или цикл прерывается раньше
Линейно растёт с объёмом данных, обработка завершается
Сеанс зависает — вероятен бесконечный цикл или запрос внутри тела
Изменены только те элементы, которые планировалось
Лишние изменения или потерянные строки — ошибка в логике тела цикла
Часто задаваемые вопросы
Чем отличается «Для» от «Для каждого» в 1С?
Цикл «Для» работает со счётчиком и повторяется заданное число раз, а «Для каждого» перебирает элементы коллекции, подставляя в переменную цикла сам элемент, а не его индекс. Для обхода массивов, таблиц значений и табличных частей удобнее «Для каждого» — код короче и исключает выход за границы коллекции.
Как прервать вложенный цикл полностью?
Оператор «Прервать» завершает только тот цикл, в котором записан. Чтобы выйти из обоих уровней, введите переменную-флаг: внутренний цикл устанавливает её в Истина и прерывается, а внешний после внутреннего проверяет флаг и тоже выполняет «Прервать».
Почему цикл «Пока» зависает и не заканчивается?
Условие цикла никогда не становится ложным: переменная, участвующая в условии, не изменяется внутри тела или изменяется не в ту сторону. Проверьте отладчиком значения условия на нескольких итерациях — если они не меняются, причина найдена.
Можно ли изменять коллекцию внутри цикла «Для каждого»?
Изменять свойства текущего элемента можно и нужно — например, пересчитывать суммы в строках табличной части. А вот добавлять или удалять элементы обходимой коллекции нельзя: это нарушает обход. Для удаления используйте цикл по индексам с конца к началу.
С какого числа начинается индексация коллекций в 1С?
С нуля. Первый элемент таблицы значений или массива имеет индекс 0, последний — «Количество() минус 1». Обращение по индексу, равному количеству элементов, вызывает ошибку выхода за границы.
Что быстрее: цикл по таблице значений или запрос?
Для выборки, группировки и соединения данных из базы запрос почти всегда быстрее, потому что выполняется на стороне СУБД. Цикл оправдан для логики, которую нельзя выразить запросом: сложные вычисления по строкам, обращение к объектам, последовательная обработка с условиями.
Что важно запомнить
В 1С есть три цикла: Для по счётчику, Для каждого по коллекции и Пока по условию. Прервать завершает цикл, Продолжить переходит к следующей итерации. Не изменяйте состав коллекции внутри Для каждого, следите за условием цикла Пока и выносите обращения к базе за пределы цикла.