К статье
Практический материал

Разработка и Конфигурация · 10 мин

Как сделать цикл в 1С: подробное руководство с примерами

Как сделать цикл в 1С: синтаксис Для, Пока, Для Каждого, обход таблиц значений и запросов, прерывание и типичные ошибки с примерами кода.

Цикл в 1С — это конструкция встроенного языка, которая многократно выполняет один и тот же блок кода. Без циклов невозможно обработать табличную часть документа, перебрать результат запроса, сформировать отчёт или заполнить таблицу значений. В языке 1С существует три основных вида циклов: Для ... Цикл (счётчик), Пока ... Цикл (с условием) и Для Каждого ... Цикл (обход коллекции).

Выбор конструкции зависит от задачи. Если известно точное число повторений — используют цикл со счётчиком. Если повторять нужно до выполнения условия — цикл Пока. Если нужно перебрать строки таблицы, массив или выборку запроса — удобнее всего Для Каждого или метод Следующий() у выборки.

Ниже разобран синтаксис каждого вида, практические примеры обхода типовых коллекций, способы досрочного выхода и ошибки, которые чаще всего приводят к зависанию сеанса или неверным данным.

Цикл Для: когда известно число повторений

Цикл со счётчиком применяется, когда заранее известно, сколько раз нужно выполнить код. Переменная-счётчик автоматически увеличивается на единицу при каждой итерации.

Для Счетчик = 1 По 10 Цикл

Сообщить("Итерация номер " + Счетчик);

КонецЦикла;

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

КоличествоСтрок = ТаблицаТоваров.Количество();

Для Индекс = 0 По КоличествоСтрок - 1 Цикл

Строка = ТаблицаТоваров[Индекс];

Строка.Сумма = Строка.Цена * Строка.Количество;

КонецЦикла;

Обратите внимание на типичный нюанс: индексация коллекций в 1С начинается с нуля, поэтому верхняя граница — это Количество() - 1. Ошибка «на единицу» здесь — самая частая причина обращения к несуществующей строке.

Цикл Пока: повторение по условию

Цикл Пока выполняется, пока условие истинно. Проверка происходит перед каждой итерацией, поэтому если условие ложно сразу — тело цикла не выполнится ни разу.

Остаток = 1000;

Пока Остаток > 0 Цикл

Остаток = Остаток - 150;

КонецЦикла;

Главная опасность этого цикла — бесконечное выполнение. Если внутри тела условие никак не меняется, сеанс зависнет, и работу придётся прерывать принудительно. Перед запуском убедитесь, что на каждой итерации значение, участвующее в условии, изменяется в сторону выхода.

Защита от бесконечного цикла

Простой приём — добавить счётчик итераций с аварийным выходом:

Итераций = 0;

Пока Условие Цикл

Итераций = Итераций + 1;

Если Итераций > 100000 Тогда

ВызватьИсключение "Превышен лимит итераций цикла";

КонецЕсли;

// рабочий код

КонецЦикла;

Цикл Для Каждого: обход коллекций

Конструкция Для Каждого ... Из ... Цикл — основной способ перебора строк табличных частей, таблиц значений, массивов, списков значений и структур. На каждой итерации переменная цикла содержит очередной элемент коллекции.

Для Каждого СтрокаТовара Из Документ.Товары Цикл

Если СтрокаТовара.Количество = 0 Тогда

Сообщить("Не заполнено количество в строке " + СтрокаТовара.НомерСтроки);

КонецЕсли;

КонецЦикла;

Для массива синтаксис аналогичен:

Для Каждого Элемент Из МассивКонтрагентов Цикл

ОбработатьКонтрагента(Элемент);

КонецЦикла;

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

Типичные ошибки
01
Удаление строк внутри цикла Для Каждого по той же коллекции — пропускаются элементы или возникает ошибка
02
Обход с индекса 1 вместо 0 — первая строка игнорируется, последняя итерация обращается за границу
03
Изменение переменной условия забыто в цикле Пока — сеанс зависает
04
Использование Найти() внутри цикла вместо отбора — резкое падение производительности на больших таблицах

Обход результата запроса

Выборка из запроса — не коллекция в привычном смысле, поэтому её обходят специальным способом через метод Следующий() в цикле Пока:

Запрос = Новый Запрос;

Запрос.Текст = "ВЫБРАТЬ Номенклатура, Количество ИЗ РегистрНакопления.Товары.Остатки";

Выборка = Запрос.Выполнить().Выбрать();

Пока Выборка.Следующий() Цикл

Сообщить(Выборка.Номенклатура);

КонецЦикла;

Альтернатива — выгрузить результат в таблицу значений и обойти её через Для Каждого. Это удобно, если данные нужно обходить несколько раз или модифицировать:

ТаблицаОстатков = Запрос.Выполнить().Выгрузить();

Для Каждого СтрокаОстатка Из ТаблицаОстатков Цикл

СтрокаОстатка.Количество = СтрокаОстатка.Количество * 2;

КонецЦикла;

Сравнение вариантов

Выборка + Пока Следующий()

Экономит память на больших результатах, но обход только один раз и только вперёд

Выгрузить() + Для Каждого

Позволяет многократный обход, сортировку и изменение данных, но загружает весь результат в память

Прервать и Продолжить: управление внутри цикла

Оператор Прервать полностью прекращает выполнение цикла — удобно при поиске, когда нужный элемент уже найден и дальнейший перебор не нужен:

Для Каждого Строка Из ТаблицаДокументов Цикл

Если Строка.Номер = ИскомыйНомер Тогда

НайденнаяСтрока = Строка;

Прервать;

КонецЕсли;

КонецЦикла;

Оператор Продолжить пропускает остаток текущей итерации и переходит к следующей. Это упрощает код, избавляя от глубокой вложенности условий:

Для Каждого Строка Из ТаблицаТоваров Цикл

Если Строка.ПометкаУдаления Тогда

Продолжить;

КонецЕсли;

// обработка только актуальных строк

КонецЦикла;

Порядок действий
  1. 1
    Определите тип задачи: фиксированное число повторений, условие или обход коллекции
  2. 2
    Выберите конструкцию: Для, Пока или Для Каждого
  3. 3
    Напишите тело цикла и при необходимости добавьте Прервать или Продолжить
  4. 4
    Проверьте границы: индексация с нуля, изменение условия в Пока
  5. 5
    Запустите на тестовой копии базы и убедитесь, что результат и время выполнения соответствуют ожиданиям

Вложенные циклы и производительность

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

Главный риск вложенных циклов — квадратичный рост времени выполнения. Классическая ошибка — поиск строки методом Найти() внутри цикла по другой таблице: на таблицах в тысячи строк обработка растягивается на минуты. Вместо этого заранее постройте соответствие (ключ — значение поиска, значение — строка) и обращайтесь к нему внутри цикла за постоянное время.

Индекс = Новый Соответствие;

Для Каждого СтрокаЦен Из ТаблицаЦен Цикл

Индекс.Вставить(СтрокаЦен.Номенклатура, СтрокаЦен.Цена);

КонецЦикла;

Для Каждого СтрокаТовара Из ТаблицаТоваров Цикл

Цена = Индекс[СтрокаТовара.Номенклатура];

Если Цена <> Неопределено Тогда

СтрокаТовара.Сумма = СтрокаТовара.Количество * Цена;

КонецЕсли;

КонецЦикла;

Если цикл обрабатывает данные в транзакции и затрагивает тысячи объектов, фиксируйте транзакцию порциями и измеряйте время выполнения на копии базы: длинные транзакции вызывают блокировки и конфликты у других пользователей.
ПЗ
Практическое замечание

Проверка результата и отладка цикла

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

Контрольные точки

Как проверить результат

Пустая таблица на входе
Норма

Цикл завершается без ошибок, код после цикла работает

Тревожный признак

Ошибка обращения к элементу или зависание

Число обработанных строк
Норма

Совпадает с количеством строк коллекции

Тревожный признак

На одну строку больше или меньше — ошибка в границах

Время выполнения
Норма

Линейно растёт с объёмом данных

Тревожный признак

Резко ускоряется рост — внутри цикла скрытый поиск или запрос

Частые вопросы о циклах в 1С

Можно ли изменить шаг счётчика в цикле Для, например перебирать через 2?

Нет, в цикле Для шаг всегда равен единице. Если нужен другой шаг, используйте цикл Пока с ручным увеличением переменной: Счетчик = Счетчик + 2 внутри тела цикла.

Как выйти сразу из двух вложенных циклов?

Штатного оператора для этого нет. Обычно вводят флаг-переменную: внутренний цикл прерывают Прервать и устанавливают флаг, а внешний проверяет флаг и тоже выполняет Прервать. Альтернатива — вынести вложенные циклы в процедуру и выходить через Возврат.

Чем отличается обход табличной части через Для Каждого и по индексу?

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

Как удалить часть строк таблицы значений в цикле?

Нельзя удалять строки прямо в цикле Для Каждого. Соберите удаляемые строки в отдельный массив и удалите их после цикла, либо обходите таблицу по индексу от последней строки к первой и удаляйте методом Удалить(Индекс).

Можно ли внутри цикла выполнять запрос к базе?

Технически можно, но это серьёзный антипаттерн: каждый запрос — обращение к СУБД, и на тысяче итераций обработка займёт неприемлемое время. Правильный подход — получить все нужные данные одним запросом до цикла и обращаться к ним через соответствие или отбор.

Что делать, если цикл завис и сеанс не отвечает?

Для файловой базы сеанс можно завершить через диспетчер задач, для клиент-серверной — через консоль кластера, завершив сеанс. После этого найдите причину: чаще всего условие цикла Пока не изменяется внутри тела или нарушена логика выхода из рекурсивной обработки.

Итог

Что важно запомнить

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