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