При отказе от модальных вызовов и переходе на асинхронную работу с файловой системой в 1С разработчики часто сталкиваются с ситуацией, когда код продолжения вычисления срабатывает раньше, чем завершаются все асинхронные проверки. Проанализируем, почему возникает эта проблема и какими способами можно организовать корректную последовательную обработку результатов поиска файлов.
Рассмотрим распространенный пример. Разработчик вызывает метод НачатьПоискФайлов, а в обработчике оповещения запускает цикл Для Каждого, внутри которого вызывает немодальную проверку НачатьПроверкуЭтоКаталог для каждого найденного элемента.
Посмотрим на такой пример кода:
&НаКлиенте
Процедура ЗаполнитьТаблицуИмен()
Оповещение = Новый ОписаниеОповещения("ЗаполнитьТаблицуИмен_ПоискФайлов", ЭтаФорма);
НачатьПоискФайлов(Оповещение, ИсходныйПуть, "*.*", Истина);
КонецПроцедуры
&НаКлиенте
Процедура ЗаполнитьТаблицуИмен_ПоискФайлов(НайденныеФайлы, ДополнительныеПараметры) Экспорт
ТаблицаИмен.Очистить();
Для Каждого НайденныйФайл Из НайденныеФайлы Цикл
Оповещение = Новый ОписаниеОповещения("ЗаполнитьТаблицуИмен_ПроверкаНаКаталог", ЭтаФорма, НайденныйФайл);
НайденныйФайл.НачатьПроверкуЭтоКаталог(Оповещение);
КонецЦикла;
КонецПроцедуры
&НаКлиенте
Процедура ЗаполнитьТаблицуИмен_ПроверкаНаКаталог(ЭтоКаталог, ДополнительныеПараметры) Экспорт
НоваяСтрока = ТаблицаИмен.Добавить();
НоваяСтрока.ЭтоКаталог = ЭтоКаталог;
НоваяСтрока.Имя = ДополнительныеПараметры.Имя;
НоваяСтрока.Путь = ДополнительныеПараметры.Путь;
КонецПроцедуры
В этом примере процедура ЗаполнитьТаблицуИмен_ПоискФайлов отправляет множество асинхронных запросов и мгновенно завершает свою работу. Сам цикл не ждет ответа от каждого вызова НачатьПроверкуЭтоКаталог. В результате процедуры-обработчики ЗаполнитьТаблицуИмен_ПроверкаНаКаталог срабатывают асинхронно по мере готовности ответа системы, и у разработчика нет точки в коде, где можно с уверенностью сказать, что таблица полностью заполнена и можно выполнять следующие действия.
Разберем основные подходы к решению этой задачи, от оптимизации алгоритма до использования современных асинхронных механизмов платформы 1С.
Выясним важную особенность работы с объектом Файл. Метод НачатьПоискФайлов передает в процедуру оповещения массив уже сформированных объектов типа Файл. У объекта Файл метод ЭтоКаталог() или свойство ЭтоКаталог() доступно для получения данных без совершения повторного асинхронного вызова.
Таким образом, делать отдельные асинхронные запросы внутри цикла не требуется вовсе. Достаточно прочитать свойство объекта прямо в обработчике поиска:
&НаКлиенте
Процедура ЗаполнитьТаблицуИмен_ПоискФайлов(НайденныеФайлы, ДополнительныеПараметры) Экспорт
ТаблицаИмен.Очистить();
Для Каждого НайденныйФайл Из НайденныеФайлы Цикл
НоваяСтрока = ТаблицаИмен.Добавить();
НоваяСтрока.ЭтоКаталог = НайденныйФайл.ЭтоКаталог();
НоваяСтрока.Имя = НайденныйФайл.Имя;
НоваяСтрока.Путь = НайденныйФайл.Путь;
КонецЦикла;
// В этой точке таблица полностью заполнена, можно вызывать итоговый код
ПослеЗаполненияТаблицы();
КонецПроцедуры
Если асинхронная операция действительно требует времени и вызывать её приходится в цикле, применяют подход со счетчиком обработанных элементов.
В современных версиях платформы 1С (начиная с 8.3.18) асинхронная работа существенно упростилась за счет появления ключевых слов Асинх и Ждать. Это позволяет избавиться от множества процедур с объектом ОписаниеОповещения и писать линейный код.
Рассмотрим, как выглядит тот же поиск файлов с использованием синхронизации вызовов:
&НаКлиенте
Асинх Процедура ЗаполнитьТаблицуИменAsync()
ТаблицаИмен.Очистить();
// Используем асинхронную функцию с оператором Ждать
НайденныеФайлы = Ждать НайтиФайлыАсинх(ИсходныйПуть, "*.*", Истина);
Для Каждого НайденныйФайл Из НайденныеФайлы Цикл
НоваяСтрока = ТаблицаИмен.Добавить();
НоваяСтрока.ЭтоКаталог = НайденныйФайл.ЭтоКаталог();
НоваяСтрока.Имя = НайденныйФайл.Имя;
НоваяСтрока.Путь = НайденныйФайл.Путь;
КонецЦикла;
// Код выполняется последовательно
ВыполнитьДействияПослеЗаполнения();
КонецПроцедуры
Если каталог с файлами находится на сервере или в сетевом ресурсе, доступном серверной части 1С, всю логику поиска и анализа структуры каталогов целесообразно перенести в контекст &НаСервере.
На сервере метод НайтиФайлы() работает мгновенно и синхронно. Вызовы на сервере возвращают клиенту уже готовый массив или заминированную таблицу значений, что исключает необходимость асинхронной обработки на стороне клиента.
Асинхронные методы поиска необходимы в клиенте (особенно в веб-клиенте или в режиме запрета модальных вызовов). Однако при получении массива результатов поиска повторные асинхронные запросы для каждого файла обычно не требуются. Для контроля порядка выполнения алгоритмов следует использовать современные конструкции Асинх / Ждать или переносить файловые операции на сторону сервера.