Асинхронные вызовы
Основное преимущество асинхронных вызовов перед синхронными в том, что они не блокируют пользовательский интерфейс. Если вы синхронно запускаете процедуру или функцию, которая продолжительно выполняет какие-то действия, то ваш пользовательский интерфейс на это время будет недоступен.
Можно сказать, что построение отчета в фоновом режиме (СкомпоноватьРезультат(РежимКомпоновкиРезультата.Фоновый)) и есть асинхронный вызов. Правда тут надо оговориться, что все-таки это лишь эмуляция асинхронного вызова, как и реализация, которую я покажу.
Характеристики реализации
1. Обычное и управляемое приложения
2. Клиент-серверный и файловый режим
3. Асинхронное выполнение серверных процедур и функций
4. Получение возвращаемого функцией значения
5. Получение описания произошедшей ошибки
6. Отмена асинхронного вызова
7. Слежение за завершением асинхронного вызова
8. Слежение за ходом выполнения асинхронного вызова
Требования к асинхронно вызываемым функциям и процедурам
Асинхронно вызываемые функции и процедуры должны быть экспортными, и могут находиться в следующих модулях:
- Модули менеджеров объектов
- Глобальные и неглобальные серверные общие модули
Параметры в этих функциях и процедурах должны иметь возможность передаваться с клиента на сервер. Возвращаемые значения в этих функциях должны иметь возможность передаваться с сервера на клиент.
По факту можно сказать, что требования такие же, как и к функциям и процедурам, находящимся в общих серверных модулях с признаком ВызовСервера.
Общий модуль АсинхронныеВызовы
В этом модуле находятся процедуры и функции программного интерфейса асинхронных вызовов. Только с этим модулем вам придется работать при совершении асинхронных вызовов.
Программный интерфейс состоит из следующих процедур и функций:
//Клиент
Функция ВызватьФункцию(ИмяФункции, Параметры = Неопределено, Форма = Неопределено, ИмяОбработчикаЗавершения = Неопределено, ИмяОбработчикаХодаВыполнения = Неопределено, Контекст = Неопределено, АвтоотменаЧерез = Неопределено) Экспорт
Функция ВызватьПроцедуру(ИмяПроцедуры, Параметры = Неопределено, Форма = Неопределено, ИмяОбработчикаЗавершения = Неопределено, ИмяОбработчикаХодаВыполнения = Неопределено, Контекст = Неопределено, АвтоотменаЧерез = Неопределено) Экспорт
Процедура Отменить(АсинхронныйВызовИлиМассив) Экспорт
Процедура ОтменитьВсе() Экспорт
//Сервер
Процедура ОбновитьЗначениеХодаВыполнения(ЗначениеХодаВыполнения) Экспорт
Процедура ДобавитьСообщениеХодаВыполнения(Сообщение) Экспорт
Структура АсинхронныйВызов
Вокруг данной структуры и происходят все действия - в ней хранится вся информация об асинхронном вызове.
Постоянные свойства:
Идентификатор - идентификатор асинхронного вызова (по факту идентификатор фонового задания)
ИмяФункцииИлиПроцедуры - полное имя серверной функции или процедуры
ЭтоФункция - признак того, что запускали функцию и будет возвращаемое значение
Параметры - массив параметров, который мы передали при асинхронном вызове функции или процедуры
Форма - форма, в модуле которой находятся процедуры обработчики
ИмяОбработчикаЗавершения - имя экспортной процедуры находящейся в модуле формы, которая будет обрабатывать завершение выполнения асинхронного вызова
ИмяОбработчикаХодаВыполнения - имя экспортной процедуры находящейся в модуле формы, которая будет обрабатывать ход выполнения асинхронного вызова
АвтоотменаЧерез - количество секунд, через которое необходимо автоматически отменить асинхронный вызов
НачатВ - время начала асинхронного вызова
Контекст - произвольная дополнительная информация, которая может быть связана с асинхронным вызовом
Изменяемые свойства:
Состояние - состояние асинхронного вызова (Активен, Завершен, Отменен, ЗавершенСОшибкой)
ВозвращенноеЗначение - значение возращенное функцией (для процедур этого свойства нет)
ЗначениеХодаВыполнения - значение хода выполнения, например, процент завершения
СообщенияХодаВыполнения - все сообщения, которые передавала асинхронно вызванная функция или процедура
ОписаниеОшибки - описание ошибки произошедшей при асинхронном вызове
Вам не следует самим изменять значения в этой структуре.
Асинхронный вызов процедуры или функции
Асинхронно вызвать процедуру или функцию можно только с клиента. Для этого используются две функции: ВызыватьФункцию и ВызыватьПроцедуру. Обе они возвращают структуру АсинхронныйВызов, но вы можете не обрабатывать результат, который они возвращают, если используете обработчики или не хотите следить за судьбой вызова.
Вот самый простой асинхронный вызов:
АсинхронныеВызовы.ВызватьПроцедуру("ОбщийМодульСервер.КакаяТоПроцедура");
Если процедура или функция имеет входящие параметры, то такой вызов будет выглядеть так:
ПараметрыВызова = Новый Массив;
ПараметрыВызова.Добавить("ЗначениеПараметра");
АсинхронныеВызовы.ВызватьФункцию("Обработки.КакаяТоОбработка.КакаяТоФункция", ПараметрыВызова);
Отмена асинхронного вызова
Асинхронный вызов можно отменить вручную, примерно так:
АсинхронныеВызовы.Отменить(АсинхронныйВызов); //Ранее значение в переменную АсинхронныйВызов было получено из функции ВызватьФункцию или ВызватьПроцедуру
В качестве параметра можно передать структуру АсинхронныйВызов, уникальный идентификатор вызова, строку содержащую уникальный идентификатор вызова или Массив, который может содержать все ранее перечисленное.
Можно отменить сразу все асинхроные вызовы, вот так:
АсинхронныеВызовы.ОтменитьВсе();
При этом можно сказать, что в обоих примерах выше, сам отменяющий вызов является асинхронным, т.е. после его завершения состояние асинхронного вызова не изменится сразу.
Также асинхронный вызов можно отменить автоматически по истечении заданного времени, примерно так:
АсинхронныеВызовы.ВызватьПроцедуру("ОбщийМодульСервер.КакаяТоПроцедура",,,,,,60); //Если за 60 секунд вызов не будет завершен, то он будет автоматически отменен
Слежение за завершением асинхронного вызова
Для того, чтобы отследить момент, когда асинхронный вызов будет завершен (нормально, с ошибкой или будет отменен) вам необходимо сделать следующее.
Во-первых, вы должны реализовать процедуру обработчик завершения асинхронного вызова.
Для обычной формы и асинхронно вызванной процедуры обработчик будет выглядеть так:
Процедура ОбработчикЗавершения(АсинхронныйВызов, Состояние, ОписаниеОшибки) Экспорт
Для управляемой формы и асинхронно вызванной функции обработчик будет выглядеть так:
&НаКлиенте
Процедура ОбработчикЗавершения(АсинхронныйВызов, Состояние, ВозвращенноеЗначение, ОписаниеОшибки) Экспорт
Имя обработчика вы выбираете сами.
Во-вторых, вы должны при асинхронном вызове передать в параметрах вызывающую форму и имя обработчика завершения из модуля этой формы, примерно так:
АсинхронныеВызовы.ВызватьФункцию("Обработки.КакаяТоОбработка.КакаяТоФункция",, ЭтаФорма, "ОбработчикЗавершения");
Когда асинхронный вызов завершится (нормально, с ошибкой или будет отменен), то будет вызван наш обработчик завершения.
Вы можете узнать о завершении асинхронного вызова без подключения обработчика завершения. Для этого вы должны сохранить структуру АсинхронныйВызов, которую возвращают функции ВызватьФункцию и ВызватьПроцедуру, и периодически или по нажатию кнопки проверять значение свойства Состояние.
Слежение за ходом выполнения асинхронного вызова
Асинхронный вызов может выполняться долго и иногда хотелось бы понимать сколько уже выполнено, что именно уже выполнено, ну и так далее.
Для решения этой задачи я ввел два термина: значение хода выполнения и сообщения хода выполнения.
Значение хода выполнения - это значение, которое характеризует завершенность асинхронного вызова. Это может быть процент выполнения, количество созданных объектов. Причем не обязательно это должно быть число, это может быть структура, которая содержит несколько значений и не обязательно числовых.
Сообщения хода выполнения - это сообщения, которые могут помочь в понимании того, что уже выполнено. Это может быть логирование асинхронного вызова или какие-то важные сообщения для пользователя. Причем не обязательно это должна быть строка, это может быть структура которая содержит несколько значений и не обязательно строковых.
Значение и сообщения хода выполнения - это разные вещи. Значение в каждый момент только одно, а вот сообщений может быть много.
Для того, чтобы можно было обрабатывать значение и сообщения хода выполнения, функция или процедура, вызываемая асинхронно, должна их передавать специальным образом. Для этого реализованы 2 процедуры ОбновитьЗначениеХодаВыполнения и ДобавитьСообщениеХодаВыполнения.
Примерно вот так их можно использовать:
Процедура ВыполнитьРаботу() Экспорт
Для Сч = 1 По 100 Цикл
//Код выполняющий основную работу
АсинхронныеВызовы.ОбновитьЗначениеХодаВыполнения(Сч); //Процент выполнения
АсинхронныеВызовы.ДобавитьСообщениеХодаВыполнения("Выполнили " + Сч + "%");
КонецЦикла;
КонецПроцедуры
Итак, если ваша функция или процедура, вызываемая асинхронно, обновляет значение или добавляет сообщения хода выполнения, то вы можете их отслеживать. Для этого нужно сделать следующее.
Во-первых, вам необходимо реализовать обработчик хода выполнения асинхронного вызова. Для обычной формы он будет выглядеть так:
Процедура ОбработчикХодаВыполнения(АсинхронныйВызов, Значение, Сообщения) Экспорт
Имя обработчика вы выбираете сами.
Во-вторых, вы должны при асинхронном вызове передать в параметрах вызывающую форму и имя обработчика хода выполнения из модуля этой формы, примерно так:
АсинхронныеВызовы.ВызватьФункцию("Справочники.КакойТоСправочник.КакаяТоФункция", ПараметрыВызова, ЭтаФорма, , "ОбработчикХодаВыполнения");
Обработчик хода выполнения будет вызываться тогда, когда обновилось значение хода выполнения или появились новые сообщения хода выполнения.
Вы можете узнать о ходе выполнения асинхронного вызова без подключения обработчика хода выполнения. Для этого вы должны сохранить структуру АсинхронныйВызов, которую возвращают функции ВызватьФункцию и ВызватьПроцедуру, и периодически или по нажатию кнопки проверять значение свойств ЗначениеХодаВыполнения и СообщенияХодаВыполнения. Если вы хотите видеть только новые сообщения, еще не обработанные вами, то вы должны очищать их.
АсинхронныйВызов.СообщенияХодаВыполнения.Очистить();
Демонстрационный пример
В демонстрационной конфигурации АсинхронныеВызовы.cf вы найдете пример для обычного приложения.
И для управляемого приложения.
Поэксперементируйте - это интересно.
Реализация
Можно сказать, что вся реализация строится на следующем:
- Фоновые задания и метод ПолучитьСообщенияПользователю
- Оператор Выполнить
- Глобальный обработчик ожидания
- Сериализация/десериализация с помощью ЗначениеВСтрокуВнутр/ЗначениеИзСтрокиВнутр
Более подробно вы всегда можете посмотреть реализацию в АсинхронныеВызовы.cf
Внедрение
Для внедрения в конфигурацию необходимо перенести 4 общих модуля:
- АсинхронныеВызовы
- АсинхронныеВызовыКлиент
- АсинхронныеВызовыСервер
- АсинхронныеВызовыКлиентГлобальный
А так же добавить в модули управляемого и обычного приложений экспортную переменную НезавершенныеАсинхронныеВызовы.
Заключение
Надеюсь, описанный в статье материал будет вам полезен.
Спасибо за внимание.