Массив между сервером и клиентом: возврат из функции, хранилище, JSON
Содержание
Чтобы передать массив с сервера на клиент в 1С, верните его из функции с директивой &НаСервереБезКонтекста (или из общего модуля с флагом «Вызов сервера») и примите результат в клиентской процедуре. Работает, если внутри массива лежат только сериализуемые значения: строки, числа, даты, булево, ссылки, структуры, соответствия и вложенные массивы.
Типичная ситуация: программист пишет обработку, на сервере собирает список номенклатуры в ТаблицаЗначений, возвращает её в форму — и получает ошибку. Причина в том, что не всё, что живёт на сервере 1С: Предприятия, может пересечь границу клиент–сервер. Разберём, что проходит, что нет и как передавать данные быстро.
Как устроен обмен данными между сервером и клиентом в 1С
Код управляемой формы выполняется в двух местах. Клиентские процедуры (&НаКлиенте) работают на компьютере пользователя, серверные (&НаСервере, &НаСервереБезКонтекста) — в рабочем процессе сервера или в файловом режиме внутри того же процесса, но по тем же правилам.
Каждый вызов сервера с клиента — это отдельный сетевой запрос. Параметры сериализуются на клиенте, передаются, восстанавливаются на сервере. Результат функции проделывает обратный путь. Поэтому объект на клиенте после вызова — всегда копия, а не та же самая коллекция.
Директива влияет на объём передаваемого:
| Директива | Что уходит на сервер | Когда использовать |
|---|---|---|
&НаСервере | параметры + все данные формы (реквизиты) | нужно читать или менять реквизиты формы |
&НаСервереБезКонтекста | только параметры | получить данные, не трогая форму |
| Общий модуль с флагом «Вызов сервера» | только параметры | код нужен в нескольких формах |
💡 Для «принести массив на клиент» почти всегда хватает &НаСервереБезКонтекста. Контекст формы гонять туда-обратно незачем.
Минимальный рабочий пример
&НаКлиенте
Процедура ЗаполнитьСписок(Команда)
СписокКодов = ПолучитьКодыНаСервере();
Для Каждого Код Из СписокКодов Цикл
Сообщить(Код);
КонецЦикла;
КонецПроцедуры
&НаСервереБезКонтекста
Функция ПолучитьКодыНаСервере()
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ ПЕРВЫЕ 100
| Номенклатура.Код КАК Код
|ИЗ
| Справочник.Номенклатура КАК Номенклатура";
Возврат Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("Код");
КонецФункции
ВыгрузитьКолонку() возвращает обычный Массив, поэтому результат спокойно уходит на клиент. Сама таблица значений осталась на сервере.
Какие типы значений передаются, а какие нет
Платформа передаёт между клиентом и сервером только те типы, которые умеет сериализовать и которые существуют на клиенте. Массив сам по себе проходит. Проблемы создаёт его содержимое.
✅ Передаются без подготовки:
Строка,Число,Дата,Булево,Неопределено,Null;- ссылки на объекты базы (
СправочникСсылка.Контрагенты,ДокументСсылка.РеализацияТоваровУслуги т. п.); Массив,Структура,Соответствиеи их фиксированные варианты;УникальныйИдентификатор,ХранилищеЗначения,ОписаниеТипов.
❌ Не передаются на клиент:
ТаблицаЗначений,ДеревоЗначений;Запрос,РезультатЗапроса,ВыборкаИзРезультатаЗапроса;- объекты (
СправочникОбъект,ДокументОбъект) и менеджеры записей; - наборы записей регистров.
Правило проверяется просто: если тип нельзя создать конструктором Новый в клиентской процедуре тонкого клиента
, на клиент его не передать. Попытка написать на клиенте Новый ТаблицаЗначений даёт ошибку Тип не определен (ТаблицаЗначений) — тот же сигнал, что тип там не живёт.
Ссылка на клиенте — это только ссылка. Прочитать через точку реквизит (Ссылка.ИНН) на клиенте нельзя: каждое обращение к данным базы идёт через сервер. Нужные поля собирайте в структуру ещё на сервере.
Массив структур вместо массива объектов
Самый практичный формат для списков — массив структур с готовыми значениями:
&НаСервереБезКонтекста
Функция ДанныеКонтрагентов(МассивСсылок)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| К.Ссылка КАК Ссылка,
| К.Наименование КАК Наименование,
| К.ИНН КАК ИНН
|ИЗ
| Справочник.Контрагенты КАК К
|ГДЕ
| К.Ссылка В (&Список)";
Запрос.УстановитьПараметр("Список", МассивСсылок);
Результат = Новый Массив;
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Строка = Новый Структура("Ссылка, Наименование, ИНН");
ЗаполнитьЗначенияСвойств(Строка, Выборка);
Результат.Добавить(Строка);
КонецЦикла;
Возврат Результат;
КонецФункции
На клиенте к полям обращаются как Результат[0].ИНН. Ни одного лишнего вызова сервера.
Передача параметров «по ссылке» и роль Знач
Массив можно не только вернуть из функции, но и передать параметром. Если параметр серверной процедуры объявлен без Знач, изменения, сделанные на сервере, вернутся на клиент после вызова.
&НаКлиенте
Процедура Обработать(Команда)
Коды = Новый Массив;
Коды.Добавить("00-001");
ДополнитьНаСервере(Коды);
// Здесь в Коды уже два элемента
КонецПроцедуры
&НаСервереБезКонтекста
Процедура ДополнитьНаСервере(Коды)
Коды.Добавить("00-002");
КонецПроцедуры
Работает это не как настоящая ссылка в памяти. Платформа сериализует параметр, отправляет, а после вызова отправляет его обратно и подменяет значение на клиенте. Если на клиенте в другой переменной хранилась ссылка на тот же массив, она указывает на старый экземпляр — учитывайте это.
💡 Если сервер только читает параметр, пишите Знач:
&НаСервереБезКонтекста
Функция Сумма(Знач Числа)
Итог = 0;
Для Каждого Ч Из Числа Цикл
Итог = Итог + Ч;
КонецЦикла;
Возврат Итог;
КонецФункции
С Знач массив не путешествует обратно. Для массива на 10 элементов разницы нет, для массива на 50 000 структур — заметна.
ТаблицаЗначений: как всё-таки получить её на клиенте
Сама ТаблицаЗначений на клиент не попадёт. Но задача «показать или обработать таблицу в форме» решается тремя способами.
| Способ | Как работает | Когда подходит |
|---|---|---|
Реквизит формы типа ТаблицаЗначений | ЗначениеВРеквизитФормы() на сервере, на клиенте видна ДанныеФормыКоллекция | таблицу нужно показать пользователю |
| Массив структур | каждая строка — Структура с нужными колонками | обработать данные в клиентском коде |
| Временное хранилище | таблица лежит на сервере, клиент держит только адрес | передать данные между серверными вызовами |
Через реквизит формы
Создайте в форме реквизит ТаблицаТоваров типа ТаблицаЗначений, добавьте колонки. Заполнение — в процедуре &НаСервере, потому что нужен контекст формы:
&НаСервере
Процедура ЗаполнитьТаблицуНаСервере()
Запрос = Новый Запрос("ВЫБРАТЬ ПЕРВЫЕ 50 Ссылка КАК Номенклатура, Артикул ИЗ Справочник.Номенклатура");
ТЗ = Запрос.Выполнить().Выгрузить();
ЗначениеВРеквизитФормы(ТЗ, "ТаблицаТоваров");
КонецПроцедуры
На клиенте строки доступны как ТаблицаТоваров[0].Артикул, по ним работает Для Каждого. Но это уже не серверная таблица: метода Свернуть() и запросов к ней на клиенте нет, поэтому группировки и расчёты делайте на сервере.
Через временное хранилище
Когда клиенту нужно не содержимое, а «держать» данные между вызовами, кладите их в хранилище:
&НаСервере
Функция ПодготовитьДанные()
ТЗ = ПолучитьБольшуюТаблицу(); // ваша серверная функция
Возврат ПоместитьВоВременноеХранилище(ТЗ, УникальныйИдентификатор);
КонецФункции
Клиент получает строку-адрес, хранит её в реквизите формы и передаёт в следующий серверный вызов. Там ПолучитьИзВременногоХранилища(Адрес) возвращает таблицу. Сама таблица на клиент не идёт.
💡 Второй параметр — УникальныйИдентификатор формы. Хранилище очистится при закрытии формы, а не будет жить до конца сеанса.
Передача массива через JSON
JSON нужен, когда массив уходит во внешнюю систему, сохраняется в строковый реквизит или передаётся через HTTP-сервис. Для обмена внутри одной формы он избыточен: платформа и так сериализует массив сама.
Объекты ЗаписьJSON и ЧтениеJSON, функции ЗаписатьJSON() и ПрочитатьJSON() доступны и на сервере, и на клиенте. Они понимают массивы, структуры, соответствия со строковыми ключами и примитивы.
&НаСервереБезКонтекста
Функция МассивВJSON()
Данные = Новый Массив;
Данные.Добавить(Новый Структура("Код, Цена", "00-001", 1250));
Данные.Добавить(Новый Структура("Код, Цена", "00-002", 980));
Запись = Новый ЗаписьJSON;
Запись.УстановитьСтроку();
ЗаписатьJSON(Запись, Данные);
Возврат Запись.Закрыть();
КонецФункции
&НаКлиенте
Процедура РазобратьJSON(Команда)
Чтение = Новый ЧтениеJSON;
Чтение.УстановитьСтроку(МассивВJSON());
Данные = ПрочитатьJSON(Чтение); // структуры восстановятся
Чтение.Закрыть();
Сообщить(Данные[1].Цена); // 980
КонецПроцедуры
Строка JSON из примера: [{"Код":"00-001","Цена":1250},{"Код":"00-002","Цена":980}].
Ссылки на объекты базы ЗаписатьJSON() не запишет. Перед сериализацией замените их на УникальныйИдентификатор (Ссылка.УникальныйИдентификатор()) или код, а на приёмной стороне ищите объект заново. Даты по умолчанию пишутся в формате ISO.
Когда JSON всё-таки оправдан внутри конфигурации:
- 📦 данные нужно сохранить в реквизите типа
Строканеограниченной длины; - 🌐 тот же массив отдаёт HTTP-сервис или получает внешний сайт;
- 🧩 нужен человекочитаемый лог того, что ушло на клиент.
Как ускорить передачу больших массивов
Время передачи зависит от двух вещей: числа серверных вызовов и объёма данных в каждом. Сократить стоит оба.
- Один вызов вместо цикла вызовов. Классическая ошибка — на клиенте перебирать массив ссылок и для каждой звать сервер за реквизитом. 500 ссылок — 500 сетевых запросов. Передайте весь массив одним параметром и верните массив структур одним ответом.
- Только нужные колонки. Если на клиенте нужны код и цена, не тащите наименование, комментарий и 15 других полей. Каждое поле умножается на число строк.
&НаСервереБезКонтекставместо&НаСервере. С контекстом на сервер и обратно уходят все реквизиты формы, включая табличные части на тысячи строк.Значу читаемых параметров. Убирает обратную передачу.- Порционная выдача. Для десятков тысяч строк выдавайте данные пачками, например по 1000 элементов, и подгружайте следующую пачку по запросу пользователя.
- Длительная операция для тяжёлых расчётов. Если подготовка данных занимает минуты, вынесите её в фоновое задание, результат положите во временное хранилище, а клиент заберёт адрес по завершении.
Сравнение подходов по объёму трафика:
| Что передаём | Трафик | Вызовов сервера |
|---|---|---|
| Массив ссылок, реквизиты по одной | малый на вызов, огромный в сумме | N |
| Массив структур со всеми полями | большой | 1 |
| Массив структур только с нужными полями | минимальный | 1 |
| Адрес во временном хранилище | несколько десятков байт | 1 + последующие |
Ошибки при передаче массива и как их отладить
Почти все проблемы сводятся к трём сценариям.
Как увидеть, что реально уходит на клиент
- 🔍 Отладчик. Поставьте точку останова на строке после серверного вызова и раскройте переменную в табло: видно тип каждого элемента массива. Для отладки серверного кода в клиент-серверном варианте отладка на сервере должна быть включена.
- 📊 Замер производительности в режиме отладки показывает серверные вызовы и время их выполнения. По нему легко поймать цикл, который дёргает сервер сотни раз.
- 🧪 Проверка содержимого перед возвратом. На сервере пройдитесь по массиву и выведите
ТипЗнч()элементов вСообщить()— так за минуту находится «случайная» таблица значений, вложенная в структуру.
Ошибки сериализации часто прячутся во вложенности: массив структур, в одной из которых поле содержит РезультатЗапроса. Проверяйте не только верхний уровень.