Переход с синхронных методов работы с файлами на асинхронные — это важный этап развития любой конфигурации, особенно если планируется работа в веб-клиенте. Однако на версиях платформы ниже 8.3.18, где еще нет полноценных ключевых слов Асинх и Ждать, разработчики часто сталкиваются с трудностями при использовании механизма ОписаниеОповещения. Рассмотрим типичную ситуацию, когда попытка прочитать список файлов в цикле приводит к ошибкам, и разберем, как правильно структурировать такой код.
Проанализируем исходную ситуацию. Разработчик пытается использовать метод НачатьЧтение объекта ТекстовыйДокумент внутри обычного цикла Для ... Цикл. Рассмотрим, что происходит в этот момент под капотом платформы:
НачатьЧтение для первого файла.НачатьЧтение.ВызватьИсключение, которое прерывает выполнение всего потока еще до того, как первый файл будет фактически обработан в процедуре оповещения.Главная проблема здесь заключается в том, что асинхронные вызовы в старом стиле (через ОписаниеОповещения) не блокируют выполнение кода. Платформа пытается запустить чтение всех файлов одновременно, что в лучшем случае приведет к непредсказуемому порядку обработки, а в худшем — к ошибкам доступа к ресурсам или переполнению стека вызовов.
Чтобы последовательно обработать массив файлов на платформе 8.3.12, нам необходимо отказаться от классического цикла. Вместо него мы реализуем цепочку вызовов: следующая итерация должна запускаться только тогда, когда завершилась предыдущая. Разберем этот алгоритм по шагам.
Шаг 1. Инициация поиска файлов. Сначала мы находим все файлы в каталоге и передаем управление в процедуру обработки списка.
&НаКлиенте
Процедура ПрочитатьКаталог(Команда)
ПутьФайлов = Объект.ФайлЕксель + "\";
Оповещение = Новый ОписаниеОповещения("ПрочитатьКаталогЗавершение", ЭтотОбъект);
НачатьПоискФайлов(Оповещение, ПутьФайлов, "*.csv");
КонецПроцедуры
Шаг 2. Подготовка к последовательной обработке. В процедуре завершения поиска мы не запускаем цикл, а подготавливаем структуру данных (контейнер) и вызываем процедуру чтения первого файла.
&НаКлиенте
Процедура ПрочитатьКаталогЗавершение(НайденныеФайлы, ДополнительныеПараметры) Экспорт
Если НайденныеФайлы.Количество() = 0 Тогда
Возврат;
КонецЕсли;
// Создаем структуру для хранения состояния процесса
ПараметрыПроцесса = Новый Структура;
ПараметрыПроцесса.Вставить("МассивФайлов", НайденныеФайлы);
ПараметрыПроцесса.Вставить("ТекущийИндекс", 0);
// Запускаем чтение первого файла
ПрочитатьСледующийФайлИзСписка(ПараметрыПроцесса);
КонецПроцедуры
Шаг 3. Итеративное чтение. Мы создаем вспомогательную процедуру, которая берет файл по текущему индексу и вызывает НачатьЧтение. В ОписаниеОповещения мы передаем наши ПараметрыПроцесса, чтобы не потерять контекст (счетчик и массив).
&НаКлиенте
Процедура ПрочитатьСледующийФайлИзСписка(ПараметрыПроцесса)
Если ПараметрыПроцесса.ТекущийИндекс >= ПараметрыПроцесса.МассивФайлов.Количество() Тогда
ПоказатьПредупреждение(, "Обработка всех файлов завершена!");
Возврат;
КонецЕсли;
ТекущийФайл = ПараметрыПроцесса.МассивФайлов[ПараметрыПроцесса.ТекущийИндекс];
ПрочитанныйФайл = Новый ТекстовыйДокумент;
Оповещение = Новый ОписаниеОповещения("ПослеЧтенияФайла", ЭтотОбъект, ПараметрыПроцесса);
ПрочитанныйФайл.НачатьЧтение(Оповещение, ТекущийФайл.ПолноеИмя);
КонецПроцедуры
Шаг 4. Обработка данных и переход к следующему шагу. В финальной процедуре мы обрабатываем текст прочитанного файла, увеличиваем индекс и снова вызываем ПрочитатьСледующийФайлИзСписка.
&НаКлиенте
Процедура ПослеЧтенияФайла(ТекстДокумента, ПараметрыПроцесса) Экспорт
// Здесь ТекстДокумента — это объект ТекстовыйДокумент, который закончил чтение
// Выполняем логику разбора CSV (как в синхронном варианте)
КоличествоСтрок = ТекстДокумента.КоличествоСтрок();
// ... логика обработки строк ...
// Переходим к следующему файлу
ПараметрыПроцесса.ТекущийИндекс = ПараметрыПроцесса.ТекущийИндекс + 1;
ПрочитатьСледующийФайлИзСписка(ПараметрыПроцесса);
КонецПроцедуры
Разберем ситуацию, на которую указывают опытные разработчики в обсуждении: асинхронность на клиенте в 1С — это не многопоточность. Проанализируем важные аспекты:
НачатьЧтение позволяет интерфейсу «дышать» (пользователь может нажать кнопку «Отмена»), но не ускоряет сам процесс парсинга строк.ТекстовыйДокумент при вызове НачатьЧтение загружает файл в память целиком. Если вы обрабатываете сотни тяжелых CSV-файлов, клиентское приложение может начать потреблять значительный объем ОЗУ.&НаСервере для пакетной записи в базу данных. Это значительно эффективнее, чем делать сотни мелких серверных вызовов в цикле.Выясним причину ошибки из исходного сообщения. Помимо логической ошибки с циклом, в коде присутствовала строка ВызватьИсключение "Вызов выполнен не полностью". В асинхронной модели выполнения это исключение срабатывает мгновенно после запуска процесса чтения, но до его фактического завершения. Использовать ВызватьИсключение таким образом внутри асинхронных процедур нельзя — это прерывает стек выполнения и не дает платформе вызвать процедуру оповещения.
Подведем итог: Для работы с асинхронными методами на старых платформах (до 8.3.18) используйте событийную модель. Передавайте состояние процесса через ДополнительныеПараметры объекта ОписаниеОповещения и запускайте следующую итерацию только в обработчике завершения предыдущей. Это гарантирует стабильную работу программы и отзывчивость интерфейса.