Как правильно реализовать асинхронное чтение файлов в цикле на платформе 1С:Предприятие 8.3.12?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

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

Анализ проблемы: почему стандартный цикл не работает

Проанализируем исходную ситуацию. Разработчик пытается использовать метод НачатьЧтение объекта ТекстовыйДокумент внутри обычного цикла Для ... Цикл. Рассмотрим, что происходит в этот момент под капотом платформы:

  1. Программа заходит в цикл и вызывает метод НачатьЧтение для первого файла.
  2. Поскольку метод асинхронный, управление мгновенно возвращается в цикл, не дожидаясь завершения физического чтения файла с диска.
  3. Цикл тут же переходит ко второй итерации и снова вызывает НачатьЧтение.
  4. В коде автора также присутствует ВызватьИсключение, которое прерывает выполнение всего потока еще до того, как первый файл будет фактически обработан в процедуре оповещения.

Главная проблема здесь заключается в том, что асинхронные вызовы в старом стиле (через ОписаниеОповещения) не блокируют выполнение кода. Платформа пытается запустить чтение всех файлов одновременно, что в лучшем случае приведет к непредсказуемому порядку обработки, а в худшем — к ошибкам доступа к ресурсам или переполнению стека вызовов.

Решение №1: Использование «рекурсивного» вызова через ОписаниеОповещения

Чтобы последовательно обработать массив файлов на платформе 8.3.12, нам необходимо отказаться от классического цикла. Вместо него мы реализуем цепочку вызовов: следующая итерация должна запускаться только тогда, когда завершилась предыдущая. Разберем этот алгоритм по шагам.

Шаг 1. Инициация поиска файлов. Сначала мы находим все файлы в каталоге и передаем управление в процедуру обработки списка.


&НаКлиенте
Процедура ПрочитатьКаталог(Команда)
    ПутьФайлов = Объект.ФайлЕксель + "\";
    Оповещение = Новый ОписаниеОповещения("ПрочитатьКаталогЗавершение", ЭтотОбъект);
    НачатьПоискФайлов(Оповещение, ПутьФайлов, "*.csv");
КонецПроцедуры

Шаг 2. Подготовка к последовательной обработке. В процедуре завершения поиска мы не запускаем цикл, а подготавливаем структуру данных (контейнер) и вызываем процедуру чтения первого файла.


&НаКлиенте
Процедура ПрочитатьКаталогЗавершение(НайденныеФайлы, ДополнительныеПараметры) Экспорт
    Если НайденныеФайлы.Количество() = 0 Тогда
        Возврат;
    КонецЕсли;
    
    // Создаем структуру для хранения состояния процесса
    ПараметрыПроцесса = Новый Структура;
    ПараметрыПроцесса.Вставить("МассивФайлов", НайденныеФайлы);
    ПараметрыПроцесса.Вставить("ТекущийИндекс", 0);
    
    // Запускаем чтение первого файла
    ПрочитатьСледующийФайлИзСписка(ПараметрыПроцесса);
КонецПроцедуры

Шаг 3. Итеративное чтение. Мы создаем вспомогательную процедуру, которая берет файл по текущему индексу и вызывает НачатьЧтение. В ОписаниеОповещения мы передаем наши ПараметрыПроцесса, чтобы не потерять контекст (счетчик и массив).


&НаКлиенте
Процедура ПрочитатьСледующийФайлИзСписка(ПараметрыПроцесса)
    Если ПараметрыПроцесса.ТекущийИндекс >= ПараметрыПроцесса.МассивФайлов.Количество() Тогда
        ПоказатьПредупреждение(, "Обработка всех файлов завершена!");
        Возврат;
    КонецЕсли;
    
    ТекущийФайл = ПараметрыПроцесса.МассивФайлов[ПараметрыПроцесса.ТекущийИндекс];
    ПрочитанныйФайл = Новый ТекстовыйДокумент;
    
    Оповещение = Новый ОписаниеОповещения("ПослеЧтенияФайла", ЭтотОбъект, ПараметрыПроцесса);
    ПрочитанныйФайл.НачатьЧтение(Оповещение, ТекущийФайл.ПолноеИмя);
КонецПроцедуры

Шаг 4. Обработка данных и переход к следующему шагу. В финальной процедуре мы обрабатываем текст прочитанного файла, увеличиваем индекс и снова вызываем ПрочитатьСледующийФайлИзСписка.


&НаКлиенте
Процедура ПослеЧтенияФайла(ТекстДокумента, ПараметрыПроцесса) Экспорт
    // Здесь ТекстДокумента — это объект ТекстовыйДокумент, который закончил чтение
    // Выполняем логику разбора CSV (как в синхронном варианте)
    КоличествоСтрок = ТекстДокумента.КоличествоСтрок();
    
    // ... логика обработки строк ...
    
    // Переходим к следующему файлу
    ПараметрыПроцесса.ТекущийИндекс = ПараметрыПроцесса.ТекущийИндекс + 1;
    ПрочитатьСледующийФайлИзСписка(ПараметрыПроцесса);
КонецПроцедуры

Важные замечания по производительности и ограничениям

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

Почему в коде автора возникало исключение?

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

Подведем итог: Для работы с асинхронными методами на старых платформах (до 8.3.18) используйте событийную модель. Передавайте состояние процесса через ДополнительныеПараметры объекта ОписаниеОповещения и запускайте следующую итерацию только в обработчике завершения предыдущей. Это гарантирует стабильную работу программы и отзывчивость интерфейса.

← На главную