Асинхронный обмен данными с JavaScript (и не только) без потерь

26.12.16

Разработка - Языки и среды

Все больше и больше появляется разработок с использованием JavaScript в формах 1С. Вместе с тем достаточно интересным является вопрос передачи данных между скриптом JavaScript и программным модулем 1С. Не претендуя на оригинальность, хочу предложить несложный и практичный механизм передачи данных с использованием очереди.

Скачать файл

ВНИМАНИЕ: Файлы из Базы знаний - это исходный код разработки. Это примеры решения задач, шаблоны, заготовки, "строительные материалы" для учетной системы. Файлы ориентированы на специалистов 1С, которые могут разобраться в коде и оптимизировать программу для запуска в базе данных. Гарантии работоспособности нет. Возврата нет. Технической поддержки нет.

Наименование По подписке [?] Купить один файл
Пример к статье
.epf 7,95Kb
17
17 Скачать (1 SM) Купить за 1 850 руб.

Идея этой публикации родилась после чтения одной очень полезной статьи Javascript и 1С. Кросс-платформенное взаимодействие. В обсуждении этой публикации  была затронута интересная проблема:

... события на вебстранице возникают асинхронно, форма 1С, в общем случае, может какие-то из виртуальных кликов пропустить. Тип переменной interactionVariable лучше сделать строкой и не просто присваивать, а дописывать в неё json очередного сообщения. На стороне формы 1С реализовать очередь, в которую складывать элементы массива из interactionVariable и последовательно обрабатывать.

............

... непонятно, как поведет себя одновременный .pop() у этого стека в 1С, и .push() в javascript, при отсутствии mutex'ов.

...........

... Поведёт себя плохо, решение не идеальное, но вероятность проблем ниже.

Однако существует достаточно простой механизм, лишенный вышеуказанных недостатков.

Пусть имеется два процесса: процесс-источник, посылающий данные, и процесс-приемник, эти данные принимающий. Создадим очередь - некую упорядоченную последовательность данных, обращение к которым возможно по индексу. И для 1С и для JavaScript это может быть простой массив. Помимо очереди, сделаем два указателя на начало и конец очереди: head и tail. При возникновении необходимости процесс-источник помещает данные в хвост очереди и сдвигает указатель tailПроцесс-приемник периодически, асинхронно, проверяет наличие данных в очереди, считывает их и сдвигает указатель head. Таким образом head как бы "догоняет" tail.  Индикатором наличия данных в очереди является неравенство tail <> head. Как видим, каждый из процессов имеет доступ на запись только к своему указателю, что делает ненужным организацию какого-либо конкурентного доступа к указателям, использование семафоров-mutexов и т.п. Доступ к данным в очереди также разделен: процесс-источник помещает порцию данных до того как сдвигает tail, а процесс-источник обратится к этим данным после того как tail будет сдвинут.  Аналогичным образом можно реализовать и систему, в которой получателей будет несколько - каждый процесс-приемник должен работать со своим указателем head, разница между tail и head для каждого получателя будет означать количество еще необработанных им элементов данных.

Практическая реализация этого механизма несложна и может быть реализована в двух вариантах:

  1. Очередь - обычный буфер. В этом случае создается динамический массив, который растет с увеличением количества передаваемых порций данных. Указатели tail и head также увеличиваются. Для того, чтобы не допустить неограниченного роста массива, перед добавлением очередных данных надо сделать проверку: если очередь пуста, т.е. tailhead, то сбрасываем оба указателя в начало массива. Этот вариант подходит в том случае, если процесс-приемник в целом чаще забирает данные из очереди, чем процесс-источник их туда помещает. В противном случае head никогда не "догонит" tail и мы получим неограниченный рост буфера. Но зато этот вариант гарантирует получение всех данных процессом-приемником.
  2. Кольцевой буфер. Этот вариант лучше использовать при недостатке памяти для очереди и/или когда помещение данных происходит чаще их получения. Для этого организуется массив с фиксированным количеством элементов. Когда указатель, увеличиваясь, доходит до конца массива, то его значение меняется на начало этого же массива. При росте очереди, т.е. при увеличении  tail, необходимо проверять чтобы tail не "догнал сделав круг" указатель head. Если это должно произойти, то следует либо сдвинуть оба указателя, потеряв таким образом самые старые данные, либо не помещать новые данные в очередь, потеряв их. В первом случае мы лишаемся преимущества разделенного доступа к указателям очереди. А что касается второго случая -  программисты MS-DOS вспомнят, что именно так организован клавиатурный буфер: по адресу 0040:001E могло быть записано 15 клавиатурных нажатий, а далее происходило "переполнение буфера".

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

Текст поля HTML:

<!DOCTYPE html>
<html style="height: 100%;">
<head>	
<meta http-equiv="X-UA-Compatible" content="IE=9"/>	
<script>

var queue = Array(); // Очередь событий
var queueHead = -1; // Указатель на начало очереди
var queueTail = -1; // Указатель на конец очереди
var totalEventCount = 0; //Количество обработанных событий

//Помещение произвольных данных в очередь
function pushEvent(dataEvent){
	if (queueHead == queueTail && queueHead > -1){ //Если очередь пуста, сбросим указатели
		queueHead = queueTail = -1;
	}
	queue[queueTail + 1] = JSON.stringify(dataEvent);//Преобразуем данные в JSON
	queueTail += 1;
	totalEventCount += 1;
}

//Получение данных очереди по индексу
function getEvent(index){
	return queue[index];
}

//Обработчик события движения указателя мыши
function OnMouseMoveEvent (event) {	
   	//Поместим данные в очередь
	pushEvent({x:event.clientX, y:event.clientY});
	//Отобразим координаты
	document.getElementById("coord").textContent = "X=" + event.clientX + " Y= " + event.clientY + " Total=" + totalEventCount;
	//Уведомим форму 1С
	button1c.click();
}

</script>
</head>

<body style="height: 100%;">
<div id="coord" style="height: 100%;"></div>

//Невидимая кнопка для уведомления формы 1С
<button id="button1c" style="display: none"></button>
</body>
</html>

При движении мыши вызывается обработчик OnMouseMoveEvent, в котором происходит помещение данных о координатах в очередь и уведомление формы 1С об этом событии. Уведомление осуществляется с помощью эмуляции нажатия невидимой кнопки button1c. 

На стороне 1С обработаем событие от поля HTML:

&НаКлиенте
Процедура ХТМЛПриНажатии(Элемент, ДанныеСобытия, СтандартнаяОбработка)
	Если ДанныеСобытия.button.id="button1c" Тогда   		
		ПолучитьИОбработатьДанные();		
	КонецЕсли; 	
КонецПроцедуры

и, для надежности, получение и обработку данных добавим в обработчике ожидания:

&НаКлиенте
Процедура ПриОткрытии(Отказ)
	ПодключитьОбработчикОжидания("ПолучитьИОбработатьДанные",1);
КонецПроцедуры

Процедура получения данных:

&НаКлиенте
Процедура ПолучитьИОбработатьДанные()Экспорт
	ОкноДок=ЭтаФорма.Элементы.ХТМЛ.Документ.parentWindow;
	Если ОкноДок=Неопределено  Тогда   
		ОкноДок=ЭтаФорма.Элементы.ХТМЛ.Документ.defaultView;
	КонецЕсли; 
	
	Пока ОкноДок.queueHead<ОкноДок.queueTail Цикл //Если есть что-то в очереди
		Данные=JSON_В_Объект(ОкноДок.getEvent(ОкноДок.queueHead+1));
		ОкноДок.queueHead=ОкноДок.queueHead+1;
		ВсегоСобытий=ВсегоСобытий+1;
		Сообщить("X="+Данные.x+" Y="+Данные.y+" Всего событий="+ВсегоСобытий); 				
	КонецЦикла; 	
КонецПроцедуры // 

Преобразование полученных данных

&НаСервереБезКонтекста
Функция JSON_В_Объект(СтрокаJSON)	
	Чтение=новый ЧтениеJSON();
	Чтение.УстановитьСтроку(СтрокаJSON);
	Результат=ПрочитатьJSON(Чтение);
	Чтение.Закрыть();		
	Возврат Результат;
КонецФункции //

Конечно же, рассмотренный механизм может быть использован не только в случае взаимодействия JavaScript и 1С. Его можно применять для асинхронного обмена данными между самыми разными процессами, но при соблюдении следующих требований: очередь должна быть постоянно доступна всем участникам обмена, и добавлять данные в очередь может только один участник. 

К статье приложена обработка с вышеописанным примером.

PS: Почему-то не получилось добавить обработку-пример ни для бесплатного скачивания, ни для скачивания за 0 StartMoney. Печаль.

JavaScript очередь асинхронный обмен асинхронное взаимодействие

См. также

Языки и среды Программист Платформа 1С v8.3 Бесплатно (free)

Будем писать свои скрипты на питоне и запускать их на 1С.

15.04.2024    3606    YA_418728146    13    

62

Мобильная разработка Языки и среды 1С:Элемент Программист Бесплатно (free)

Flutter может быть использован с 1С:Предприятием для разработки кроссплатформенных мобильных приложений, обеспечивая единый интерфейс и функциональность на устройствах под управлением iOS и Android. Это позволяет создавать приложения с высокой производительностью благодаря использованию собственного движка рендеринга Flutter. Интеграция Flutter с 1С:Предприятием позволяет создавать мобильные приложения любого уровня сложности, интегрировать их в корпоративные информационные системы, а также реализовывать бизнес-логику

19.03.2024    16407    ROk_dev    69    

43

Языки и среды Программист Стажер Платформа 1С v8.3 Бесплатно (free)

Существует множество языков программирования, и каждый имеет свои особенности по работе с типами данных. Слабые, явные, динамические и другие... Но кто же здесь 1С и почему с приходом "строгой" типизации EDT 1С-программистам стоит задуматься над изменением своих привычек.

16.01.2024    6802    SeiOkami    25    

61

Языки и среды Программист Бесплатно (free)

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

08.08.2023    4055    acvatoris    6    

15

Языки и среды Программист Платформа 1С v8.3 Россия Бесплатно (free)

Написание статического анализатора для 1С традиционным способом на Си.

30.06.2023    3434    prohorp    15    

12

Языки и среды Программист Абонемент ($m)

Поставили нам задачу - вынести на отдельный сервер функционал получения заказов от клиентов по электронной почте, парсинг полученных XLS в приемлемый вид и трансформация заказов в красивый JSON, понятный нашей учетной системе на 1С. Всю эту красоту желательно запустить в отдельном докер - контейнере, по возможности не тратя лицензии, поэтому отдельно стоящую конфигурацию на БСП отвергаем сразу. Можно было бы собрать всё на Apache Airflow или Apache NiFi, но решили попробовать реализовать всю логику без Open Source, будем делать свой ETL, с Исполнителем, который в версии 3.0 научился взаимодействовать с электронной почтой по IMAP. Начнем с середины - сначала напишем скрипты, а потом соберем их в рабочую конструкцию

1 стартмани

01.06.2023    2254    0    kembrik    2    

7

Языки и среды Программист Платформа 1С v8.3 Бесплатно (free)

При работе с 1С ORM (object relation mapping) все время преследует ощущение постоянного создания монолитного приложения — один раз привязался к какой либо сущности (например, справочник Контрагенты), и весь код заполнен ссылками на эту конкретную реализацию. Можно ли независимо разрабатывать в ORM совместимые между собой справочник «Контрагентов» и использующий его документ «Платежное поручение», но при этом избежать жестких зависимостей? Спасут ли нас микросервисы? Пример на аннотациях Java демонстрирует, как это возможно делать.

13.03.2023    1263    1CUnlimited    0    

3
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. VitaliyCeban 464 26.12.16 10:25 Сейчас в теме
Учитывая что событие ПриНажатии может быть пропущено, и оно могло быть последним (не поможет обработка предыдущих событий при следующем ПриНажатии, так как его не будет), то относительная гарантия доставки, в итоге, достигается исключительно за счет следующего кода?
&НаКлиенте
Процедура ПриОткрытии(Отказ)
	ПодключитьОбработчикОжидания("ПолучитьИОбработатьДанные",1);
КонецПроцедуры

Относительная, потому что форма может быть закрыта до очередного срабатывания таймера, впрочем, это я уже утрирую )

В любом случае, решение интересное, и существенно лучше его отсутствия, спасибо.
5. Alxby 1111 26.12.16 13:56 Сейчас в теме
(1) Да, при закрытии формы мы можем потерять последние события. Но нам никто не мешает перед закрытием формы их все-таки принудительно получить. К тому же, как мне кажется, основное применение JavaScript - визуальные компоненты интерфейса, данные которых вряд ли нужны после закрытия формы.
2. igo1 269 26.12.16 13:28 Сейчас в теме
вообще вот пример как работать без обработчика ожидания, можно просто вызвать событие "нажатие", и передать туда данные

http://infostart.ru/public/338126/
3. VitaliyCeban 464 26.12.16 13:45 Сейчас в теме
(2) Прочитайте более внимательно начало этой публикации.
можно просто вызвать
оно то можно, только есть ненулевая вероятность что в 1С событие ПриНажатии вызвано НЕ будет.
Эту проблему и призвана решить данная публикация.
4. Alxby 1111 26.12.16 13:48 Сейчас в теме
(3) Спасибо, опередили с ответом:)
13. igo1 269 27.12.16 15:20 Сейчас в теме
(3) не встречал таких проблем с пропуском нажатий, но в таком случае я бы решал задачу немного иначе, реализовав гарантированную доставку.

14. Alxby 1111 27.12.16 16:12 Сейчас в теме
(13) У меня тоже была идея реализовать механизм, похожий на работу TCP (правда для решения другой задачи). Может быть напишете статью о своем варианте?
15. herfis 513 27.12.16 16:29 Сейчас в теме
(13) Каким именно образом и чем он лучше? Статья ведь как раз и рассматривает вариант реализации гарантированной доставки.
16. pbazeliuk 1967 27.12.16 17:46 Сейчас в теме
(15) Термин "гарантированная доставки" здесь не применим. Объекты жестко связаны, они не могут отдельно работать друг от друга, нет восстановления состояния, например, после падения кластера 1С.

В этом случае, решена проблема пропускания "кликов".

17. herfis 513 27.12.16 18:23 Сейчас в теме
(16) Вы пытаетесь сказать, что под термин "гарантированная доставка" подходят только внешние сервисы очередей? Не думаю, что в (13) именно это подразумевалось :)
В нашем случае "гарантированность" имеет смысл только в рамках жизненного цикла формы.
18. pbazeliuk 1967 28.12.16 10:39 Сейчас в теме
(17) Сервис очередей не упоминал вроде как. Термин "гарантированная доставка" не должен зависеть от контекста и он не применим точно здесь. Правильней назвать это "fire-and-forget".
21. herfis 513 29.12.16 10:12 Сейчас в теме
(18)
Термин "гарантированная доставка" не должен зависеть от контекста и он не применим точно здесь

Ок. Раз не должен зависеть от контекста, то вы подразумеваете его исчерпывающее определение. Дайте ссылочку, плиз. Я такого не нашел.
Везде зависит от контекста. Например, как по вашему - TCP обеспечивает "гарантированную доставку" или нет?
22. pbazeliuk 1967 29.12.16 11:49 Сейчас в теме
(21) Он обеспечивает надежную передачу данных, но "негарантированную". Термин "гарантированная доставка" появился как описание паттерна. Действительно, пример, который не связан с очередями, сложно найти.
6. herfis 513 26.12.16 15:50 Сейчас в теме
"события на вебстранице возникают асинхронно, форма 1С, в общем случае, может какие-то из виртуальных кликов пропустить"
Поясните. По какой причине 1С может пропустить клики. И в каких случаях. Где можно об этом почитать в подробностях.
В документации по "ПриНажатии" ничего такого нет. Т.е. предполагается, что ловиться должны все события.
"события на вебстранице возникают асинхронно" - ну и что с того?
7. herfis 513 26.12.16 15:55 Сейчас в теме
В общем, если пропуски в самом деле происходят, то это недоработка разработчиков 1С. Это они должны были организовывать и обслуживать очереди обрабатываемых событий, если уж задекларировали механизм получения событий от поля HTML документа.
Просто хочется убедиться, что проблема в самом деле существует. Я на самом деле далек от темы, просто интересно.
8. VitaliyCeban 464 26.12.16 16:45 Сейчас в теме
(7) Поинтересуйтесь у http://infostart.ru/profile/52749/
Самому не приходило сталкиваться с проблемой, но не доверять Евгению, учитывая созданные им решения, у меня оснований нет.
10. herfis 513 26.12.16 17:04 Сейчас в теме
(8) ИМХО, если 1С не делает пропусков, скажем, при нескольких событиях HTML с интервалом 1ms, когда в 1С каждое обрабатывается к примеру 5ms (т.е. какая-то очередь все-таки есть), то для большинства практических задач нет смысла обслуживать свою очередь событий. Когда возникнет практическая задача - поставлю эксперимент, раз тут занимаются решением проблем о которых знают по-наслышке.
Задачи Евгения, насколько я понимаю, достаточно специфичны.
9. Alxby 1111 26.12.16 16:58 Сейчас в теме
(7) Как бы то ни было, основной целью написания статьи было не решение конкретной проблемы, указанной в примере, а описание способа решения подобных ей проблем, не обязательно связанных с взаимодействием с JavaScript.
11. herfis 513 26.12.16 17:29 Сейчас в теме
(9) Соглашусь. Статью плюсанул.
Сначала зацепился за то, что queueHead пишется из двух процессов (при сбросе очереди), но получается что сброс невозможен одновременно с обработкой события в 1С. Остроумное решение проблемы мьютексов при условии частого обнуления очереди.
12. Alxby 1111 26.12.16 21:18 Сейчас в теме
(11)Здесь нелишним будет упомянуть, что в JavaScript оператор присваивания имеет ассоциативность справа налево, и
queueHead = queueTail = -1;
эквивалентно
queueTail = -1;
queueHead = -1;
что гарантирует: в процессе обнуления queueHead будет не меньше queueTail и чтение из очереди невозможно.
19. igo1 269 28.12.16 12:35 Сейчас в теме
(9) Статья очень полезная, вы не расстраивайтесь. Гора комментариев как раз об этом и говорит.
20. infostart user 20 28.12.16 14:25 Сейчас в теме
23. vaxhab 16 13.07.18 19:17 Сейчас в теме
Оставьте свое сообщение