Массив между сервером и клиентом: возврат из функции, хранилище, JSON

· 9 мин

Содержание

Чтобы передать массив с сервера на клиент в 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-сервис или получает внешний сайт;
  • 🧩 нужен человекочитаемый лог того, что ушло на клиент.

Как ускорить передачу больших массивов

Время передачи зависит от двух вещей: числа серверных вызовов и объёма данных в каждом. Сократить стоит оба.

  1. Один вызов вместо цикла вызовов. Классическая ошибка — на клиенте перебирать массив ссылок и для каждой звать сервер за реквизитом. 500 ссылок — 500 сетевых запросов. Передайте весь массив одним параметром и верните массив структур одним ответом.
  2. Только нужные колонки. Если на клиенте нужны код и цена, не тащите наименование, комментарий и 15 других полей. Каждое поле умножается на число строк.
  3. &НаСервереБезКонтекста вместо &НаСервере. С контекстом на сервер и обратно уходят все реквизиты формы, включая табличные части на тысячи строк.
  4. Знач у читаемых параметров. Убирает обратную передачу.
  5. Порционная выдача. Для десятков тысяч строк выдавайте данные пачками, например по 1000 элементов, и подгружайте следующую пачку по запросу пользователя.
  6. Длительная операция для тяжёлых расчётов. Если подготовка данных занимает минуты, вынесите её в фоновое задание, результат положите во временное хранилище, а клиент заберёт адрес по завершении.

Сравнение подходов по объёму трафика:

Что передаёмТрафикВызовов сервера
Массив ссылок, реквизиты по одноймалый на вызов, огромный в суммеN
Массив структур со всеми полямибольшой1
Массив структур только с нужными полямиминимальный1
Адрес во временном хранилищенесколько десятков байт1 + последующие
Что вам нужно сделать с данными на клиенте?

Ошибки при передаче массива и как их отладить

Почти все проблемы сводятся к трём сценариям.

Диагностика: выберите симптом

Как увидеть, что реально уходит на клиент

  • 🔍 Отладчик. Поставьте точку останова на строке после серверного вызова и раскройте переменную в табло: видно тип каждого элемента массива. Для отладки серверного кода в клиент-серверном варианте отладка на сервере должна быть включена.
  • 📊 Замер производительности в режиме отладки показывает серверные вызовы и время их выполнения. По нему легко поймать цикл, который дёргает сервер сотни раз.
  • 🧪 Проверка содержимого перед возвратом. На сервере пройдитесь по массиву и выведите ТипЗнч() элементов в Сообщить() — так за минуту находится «случайная» таблица значений, вложенная в структуру.

Ошибки сериализации часто прячутся во вложенности: массив структур, в одной из которых поле содержит РезультатЗапроса. Проверяйте не только верхний уровень.

Частые вопросы

Можно ли передать на клиент ФиксированныйМассив?
Да. ФиксированныйМассив передаётся так же, как обычный, но на клиенте его нельзя изменить. Удобно для справочных списков, которые форма только читает.
Передаётся ли Соответствие с ключами-ссылками?
Да, при обычном вызове сервера ключи-ссылки сохраняются. А вот ЗаписатьJSON() примет только строковые ключи.
Почему после серверного вызова массив на клиенте — другой объект?
Платформа передаёт копию и после вызова подставляет новый экземпляр. Другие переменные, указывавшие на старый массив, его не увидят.
Есть ли ограничение на размер передаваемого массива?
Жёсткого лимита в элементах нет, ограничивают память и сеть. Десятки тысяч структур уже заметно тормозят форму, поэтому большие объёмы выдавайте порциями или держите во временном хранилище.
Нужно ли что-то менять для веб-клиента?
Правила те же, что для тонкого клиента: передаются только сериализуемые типы, ТаблицаЗначений недоступна. Количество серверных вызовов в веб-клиенте ощущается ещё сильнее.