gifts2017

Асинхронные вызовы

Опубликовал rtnm rtnm (rtnm) в раздел Программирование - Практика программирования

В этой статье я покажу свою реализацию асинхронных вызовов серверных функций и процедур

Асинхронные вызовы

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

Можно сказать, что построение отчета в фоновом режиме (СкомпоноватьРезультат(РежимКомпоновкиРезультата.Фоновый)) и есть асинхронный вызов. Правда тут надо оговориться, что все-таки это лишь эмуляция асинхронного вызова, как и реализация, которую я покажу.

 

Характеристики реализации

1. Обычное и управляемое приложения

2. Клиент-серверный и файловый режим

3. Асинхронное выполнение серверных процедур и функций

4. Получение возвращаемого функцией значения

5. Получение описания произошедшей ошибки

6. Отмена асинхронного вызова

7. Слежение за завершением асинхронного вызова

8. Слежение за ходом выполнения асинхронного вызова

 

Требования к асинхронно вызываемым функциям и процедурам

Асинхронно вызываемые функции и процедуры должны быть экспортными, и могут находиться в следующих модулях:

- Модули менеджеров объектов

- Глобальные и неглобальные серверные общие модули

Параметры в этих функциях и процедурах должны иметь возможность передаваться с клиента на сервер. Возвращаемые значения в этих функциях должны иметь возможность передаваться с сервера на клиент.

По факту можно сказать, что требования такие же, как и к функциям и процедурам, находящимся в общих серверных модулях с признаком ВызовСервера.


Общий модуль АсинхронныеВызовы

В этом модуле находятся процедуры и функции программного интерфейса асинхронных вызовов. Только с этим модулем вам придется работать при совершении асинхронных вызовов.

Программный интерфейс состоит из следующих процедур и функций:

//Клиент
Функция ВызватьФункцию(ИмяФункции, Параметры = Неопределено, Форма = Неопределено, ИмяОбработчикаЗавершения = Неопределено, ИмяОбработчикаХодаВыполнения = Неопределено, Контекст = Неопределено, АвтоотменаЧерез = Неопределено) Экспорт
Функция ВызватьПроцедуру(ИмяПроцедуры, Параметры = Неопределено, Форма = Неопределено, ИмяОбработчикаЗавершения = Неопределено, ИмяОбработчикаХодаВыполнения = Неопределено, Контекст = Неопределено, АвтоотменаЧерез = Неопределено) Экспорт
Процедура Отменить(АсинхронныйВызовИлиМассив) Экспорт
Процедура ОтменитьВсе() Экспорт

//Сервер
Процедура ОбновитьЗначениеХодаВыполнения(ЗначениеХодаВыполнения) Экспорт
Процедура ДобавитьСообщениеХодаВыполнения(Сообщение) Экспорт


Структура АсинхронныйВызов

Вокруг данной структуры и происходят все действия - в ней хранится вся информация об асинхронном вызове.

Постоянные свойства:
    Идентификатор - идентификатор асинхронного вызова (по факту идентификатор фонового задания)
    ИмяФункцииИлиПроцедуры - полное имя серверной функции или процедуры
    ЭтоФункция - признак того, что запускали функцию и будет возвращаемое значение
    Параметры - массив параметров, который мы передали при асинхронном вызове функции или процедуры
    Форма - форма, в модуле которой находятся процедуры обработчики
    ИмяОбработчикаЗавершения - имя экспортной процедуры находящейся в модуле формы, которая будет обрабатывать завершение выполнения асинхронного вызова
    ИмяОбработчикаХодаВыполнения - имя экспортной процедуры находящейся в модуле формы, которая будет обрабатывать ход выполнения асинхронного вызова
    АвтоотменаЧерез - количество секунд, через которое необходимо автоматически отменить асинхронный вызов
    НачатВ - время начала асинхронного вызова
    Контекст - произвольная дополнительная информация, которая может быть связана с асинхронным вызовом

Изменяемые свойства:
    Состояние - состояние асинхронного вызова (Активен, Завершен, Отменен, ЗавершенСОшибкой)
    ВозвращенноеЗначение - значение возращенное функцией (для процедур этого свойства нет)
    ЗначениеХодаВыполнения - значение хода выполнения, например, процент завершения
    СообщенияХодаВыполнения - все сообщения, которые передавала асинхронно вызванная функция или процедура
    ОписаниеОшибки - описание ошибки произошедшей при асинхронном вызове

Вам не следует самим изменять значения в этой структуре.

Асинхронный вызов процедуры или функции

Асинхронно вызвать процедуру или функцию можно только с клиента.  Для этого используются две функции: ВызыватьФункцию и ВызыватьПроцедуру. Обе они возвращают структуру АсинхронныйВызов, но вы можете не обрабатывать результат, который они возвращают, если используете обработчики или не хотите следить за судьбой вызова.

Вот самый простой асинхронный вызов:

АсинхронныеВызовы.ВызватьПроцедуру("ОбщийМодульСервер.КакаяТоПроцедура");

Если процедура или функция имеет входящие параметры, то такой вызов будет выглядеть так:

ПараметрыВызова = Новый Массив;
ПараметрыВызова.Добавить("ЗначениеПараметра");
АсинхронныеВызовы.ВызватьФункцию("Обработки.КакаяТоОбработка.КакаяТоФункция", ПараметрыВызова);


Отмена асинхронного вызова

Асинхронный вызов можно отменить вручную, примерно так:

АсинхронныеВызовы.Отменить(АсинхронныйВызов); //Ранее значение в переменную АсинхронныйВызов было получено из функции ВызватьФункцию или ВызватьПроцедуру

В качестве параметра можно передать структуру АсинхронныйВызов, уникальный идентификатор вызова, строку содержащую уникальный идентификатор вызова или Массив, который может содержать все ранее перечисленное.

Можно отменить сразу все асинхроные вызовы, вот так:

АсинхронныеВызовы.ОтменитьВсе();

При этом можно сказать, что в обоих примерах выше, сам отменяющий вызов является асинхронным, т.е. после его завершения состояние асинхронного вызова не изменится сразу.

Также асинхронный вызов можно отменить автоматически по истечении заданного времени, примерно так:

АсинхронныеВызовы.ВызватьПроцедуру("ОбщийМодульСервер.КакаяТоПроцедура",,,,,,60); //Если за 60 секунд вызов не будет завершен, то он будет автоматически отменен


Слежение за завершением асинхронного вызова

Для того, чтобы отследить момент, когда асинхронный вызов будет завершен (нормально, с ошибкой или будет отменен) вам необходимо сделать следующее.

Во-первых, вы должны реализовать процедуру обработчик завершения асинхронного вызова.

Для обычной формы и асинхронно вызванной процедуры обработчик будет выглядеть так:

Процедура ОбработчикЗавершения(АсинхронныйВызов, Состояние, ОписаниеОшибки) Экспорт

Для управляемой формы и асинхронно вызванной функции обработчик будет выглядеть так:

&НаКлиенте
Процедура ОбработчикЗавершения(АсинхронныйВызов, Состояние, ВозвращенноеЗначение, ОписаниеОшибки) Экспорт

Имя обработчика вы выбираете сами.

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

АсинхронныеВызовы.ВызватьФункцию("Обработки.КакаяТоОбработка.КакаяТоФункция",, ЭтаФорма, "ОбработчикЗавершения");

Когда асинхронный вызов завершится (нормально, с ошибкой или будет отменен), то будет вызван наш обработчик завершения.

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

 

Слежение за ходом выполнения асинхронного вызова

Асинхронный вызов может выполняться долго и иногда хотелось бы понимать сколько уже выполнено, что именно уже выполнено, ну и так далее.

Для решения этой задачи я ввел два термина: значение хода выполнения и сообщения хода выполнения.

Значение хода выполнения - это значение, которое характеризует завершенность асинхронного вызова. Это может быть процент выполнения, количество созданных объектов. Причем не обязательно это должно быть число, это может быть структура, которая содержит несколько значений и не обязательно числовых.

Сообщения хода выполнения - это сообщения, которые могут помочь в понимании того, что уже выполнено. Это может быть логирование асинхронного вызова или какие-то важные сообщения для пользователя. Причем не обязательно это должна быть строка, это может быть структура которая содержит несколько значений и не обязательно строковых.

Значение и сообщения хода выполнения - это разные вещи. Значение в каждый момент только одно, а вот сообщений может быть много.

Для того, чтобы можно было обрабатывать значение и сообщения хода выполнения, функция или процедура, вызываемая асинхронно, должна их передавать специальным образом. Для этого реализованы 2 процедуры ОбновитьЗначениеХодаВыполнения и ДобавитьСообщениеХодаВыполнения.

Примерно вот так их можно использовать:

Процедура ВыполнитьРаботу() Экспорт

   Для Сч = 1 По 100 Цикл

      //Код выполняющий основную работу

      АсинхронныеВызовы.ОбновитьЗначениеХодаВыполнения(Сч); //Процент выполнения
      АсинхронныеВызовы.ДобавитьСообщениеХодаВыполнения("Выполнили " + Сч + "%");
 
   КонецЦикла;

КонецПроцедуры

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

Во-первых, вам необходимо реализовать обработчик хода выполнения асинхронного вызова. Для обычной формы он будет выглядеть так:

Процедура ОбработчикХодаВыполнения(АсинхронныйВызов, Значение, Сообщения) Экспорт

Имя обработчика вы выбираете сами.

Во-вторых, вы должны при асинхронном вызове передать в параметрах вызывающую форму и имя обработчика хода выполнения из модуля этой формы, примерно так:

АсинхронныеВызовы.ВызватьФункцию("Справочники.КакойТоСправочник.КакаяТоФункция", ПараметрыВызова, ЭтаФорма, , "ОбработчикХодаВыполнения");

Обработчик хода выполнения будет вызываться тогда, когда обновилось значение хода выполнения или появились новые сообщения хода выполнения.

Вы можете узнать о ходе выполнения асинхронного вызова без подключения обработчика хода выполнения. Для этого вы должны сохранить структуру АсинхронныйВызов, которую возвращают функции ВызватьФункцию и ВызватьПроцедуру, и периодически или по нажатию кнопки проверять значение свойств ЗначениеХодаВыполнения и СообщенияХодаВыполнения. Если вы хотите видеть только новые сообщения, еще не обработанные вами, то вы должны очищать их.

АсинхронныйВызов.СообщенияХодаВыполнения.Очистить();


Демонстрационный пример

В демонстрационной конфигурации АсинхронныеВызовы.cf вы найдете пример для обычного приложения.

Демо (обычное приложение) 

И для управляемого приложения.

Демо (управляемое приложение)

Поэксперементируйте - это интересно.


Реализация

Можно сказать, что вся реализация строится на следующем:

- Фоновые задания и метод ПолучитьСообщенияПользователю

- Оператор Выполнить

- Глобальный обработчик ожидания

- Сериализация/десериализация с помощью ЗначениеВСтрокуВнутр/ЗначениеИзСтрокиВнутр

Более подробно вы всегда можете посмотреть реализацию в АсинхронныеВызовы.cf


Внедрение

Для внедрения в конфигурацию необходимо перенести 4 общих модуля:

- АсинхронныеВызовы

- АсинхронныеВызовыКлиент

- АсинхронныеВызовыСервер

- АсинхронныеВызовыКлиентГлобальный

А так же добавить в модули управляемого и обычного приложений экспортную переменную НезавершенныеАсинхронныеВызовы.

 

Заключение

Надеюсь, описанный в статье материал будет вам полезен. 

Спасибо за внимание.

Скачать файлы

Наименование Файл Версия Размер
АсинхронныеВызовы.cf 74
.cf 203,45Kb
01.02.15
74
.cf 203,45Kb Скачать

См. также

Подписаться Добавить вознаграждение
Комментарии
2. Константин Юрин (kostyaomsk) 03.02.15 07:27
Материал будет полезен когда есть проблемы с производительностью.Или нужно на ночь запускать кучу фоновых процессов. Так обычно запускают отдельный процесс, даже в толстом клиенте 1С 8 и стараются 1С вообще не трогать.
3. Князь (Князь) 03.02.15 08:20
Весьма полезно. За реализацию оповещения пользователя о ходе процесса выполнения серверных процедур отдельное спасибо.
ybatiaev; +1 Ответить
4. Андрей Ромашов (LsrGroup) 03.02.15 10:04
В случае БСП лучше использовать подсистему ДлительныеОперации. Там все это уже реализовано.
dour-dead; pit201201; Aleksey81; YPermitin; AllexSoft; нормальный такой; +6 Ответить 1
5. Дмитрий Переверзев (webdimon) 04.02.15 11:28
Правильно я понимаю что данную схему можно будет реализовать в 8.2 и в 8.3, верно?
6. rtnm rtnm (rtnm) 04.02.15 12:29
(5) webdimon, верно, зависимостей от платформы 8.3 нет (в демонстрационном примере есть - ПоказатьПредупреждение)
7. Дмитрий Переверзев (webdimon) 04.02.15 18:43
Хотел бы уточнить, к примеру, у меня есть некий рабочий стол на упр.формах., на котором информация обновляется каждые 30 секунд, получение данных для обновления занимает 5 секунд , и все это время , тоесть 5 секунд, пользователь не может работать так как форма висит.
С вашим методом, как я понял, получиться этого избежать, верно?
8. rtnm rtnm (rtnm) 04.02.15 22:35
(7) webdimon, новых методов я не изобретал, я лишь показал одну из возможных реализаций асинхронных вызов скрывающую детали. Любая такая реализация так или иначе все равно будет использовать один и тот же подход - фоновые задания. Я думаю, что моя реализация или любая другая, можешь вам помочь. Но проще проверить, чем строить догадки. Пробуйте, а потом расскажите о результате :)
9. Евгений Сосна (pumbaE) 09.02.15 11:54
(4) LsrGroup, длительные операции - это одна из частностей асинхронного вызова, если у вас длительная операция прервется, то вы об этом вряд-ли узнаете и ваша важная задача не выполнится в полном объеме.
Aleksey81; +1 Ответить
10. Юрий Батяев (ybatiaev) 15.06.15 12:08
Весьма наглядная конфигурация. И конкретно в помощь прямо сейчас! Автору респект!!!
11. Юрий Батяев (ybatiaev) 15.06.15 13:51
сделал асинхронную функцию ЗапускПриложения() . Работает. Но никак не могу разораться как отследить её состояние :-(
в самой функции делаю архивацию баз данных с командной строки
12. rtnm rtnm (rtnm) 15.06.15 21:40
(11) ybatiaev, нужно сделать следующие вещи:
1. в самой асинхронной функции/процедуре вызывать АсинхронныеВызовы.ОбновитьЗначениеХодаВыполнения и/или АсинхронныеВызовы.ДобавитьСообщениеХодаВыполнения
2. в форме реализовать экспортную процедуру обработчик хода выполнения
3. при запуске асинхронной функции/процедуры 3-им параметром передать ссылку на форму, а 5-ым имя обработчика хода выполнения

P.S. у всех функций программного интерфейса (т.е. находящихся в модуле АсинхронныеВызовы) есть описания, так что думаю разберетесь
ybatiaev; +1 Ответить
13. Виктор Назаров (androgin) 26.09.15 01:57
(7) webdimon, тебе подойдут фоновые задания. они точно решат твою проблему.
На примере загрузки курсов валют посмотри )
14. mike mike (kozlovmv) 24.01.16 16:13
Уважаемый автор, испытывал ваши механизмы, запуском двух и более одновременных асинхронных вызовов. Мне показалось, что фактически они выполняются поочередно, в последовательности запуска.
Возможно ли достичь действительно параллельного выполнения?
15. rtnm rtnm (rtnm) 27.01.16 13:09
(14) kozlovmv, у вас клиент-серверный или файловый вариант базы?