Пакетные вызовы пользовательских функций для удаленной службы
Если пользовательские функции обращаются к удаленной службе, можно использовать шаблон пакетирования для сокращения количества сетевых вызовов удаленной службы. Для уменьшения объема сетевых операций можно объединить все вызовы в один вызов веб-службы. Это идеальное решение при пересчете электронной таблицы.
Например если пользователь обращается к вашей пользовательской функции в 100 ячейках электронной таблицы, а затем пересчитывает электронную таблицу, эта функция будет выполняться 100 раз и делать 100 сетевых вызовов. С помощью шаблона пакетирования эти вызовы можно объединить так, чтобы делать 100 расчетов в течение одного сетевого вызова.
Важно!
Обратите внимание, что настраиваемые функции доступны в Excel на следующих платформах.
- Office в Интернете
- Office для Windows
- Подписка на Microsoft 365
- Розничный бессрочный Office 2016 и более поздних версий
- корпоративные бессрочные Office 2021 и более поздних версий
- Office для Mac
Пользовательские функции Excel в настоящее время не поддерживаются в следующих приложениях:
- Office для iPad
- корпоративные бессрочные версии Office 2019 или более ранних версий в Windows
Посмотреть готовый пример
Чтобы просмотреть готовый пример, следуйте этой статье и вставьте примеры кода в собственный проект. Например, чтобы создать новый проект пользовательской функции для TypeScript, используйте генератор Yeoman для надстроек Office, а затем добавьте в проект весь код из этой статьи. Запустите код и опробуйте его.
Кроме того, можно скачать или просмотреть полный пример проекта в разделе Шаблон пакетной обработки пользовательских функций. Если вы хотите просмотреть код в целом, прежде чем читать дальше, посмотрите на файл сценария.
Создание шаблона пакетирования в этой статье
Для реализации пакетирования пользовательских функций необходимо создать три основных раздела кода.
- Операция отправки для добавления новой операции в пакет вызовов каждый раз, когда Excel вызывает пользовательскую функцию.
- Функция для выполнения удаленного запроса, когда пакет готов.
- Код сервера для ответа на пакетный запрос, вычисления всех результатов операции и возврата значений.
В следующих разделах вы узнаете, как создать код по одному примеру за раз. Рекомендуется создать новый проект пользовательских функций с помощью генератора Yeoman для надстроек Office . Сведения о создании проекта см. в статье Начало разработки пользовательских функций Excel. Вы можете использовать TypeScript или JavaScript.
Включение в пакет каждого вызова пользовательской функции
Ваши пользовательские функции вызывают удаленную службу для выполнения различных операций и вычисления требуемого результата. Это дает возможность сохранения каждой запрашиваемой операции в пакете. Далее вы узнаете, как создать функцию _pushOperation
для пакетной обработки операций. Сначала посмотрим на следующий пример кода, где показан вызов _pushOperation
из пользовательской функции.
В следующем примере пользовательская функция выполняет деление, обращаясь для этой операции к удаленной службе. Она вызывает _pushOperation
для включения операции вместе с другими операциями в пакет для удаленной службы. Операция здесь называется div2. Можно использовать для операций любую схему именования, если только в удаленной службе используется такая же схема (дополнительно об удаленной службе см. далее). Кроме того передаются аргументы, необходимые удаленной службе для выполнения операции.
Добавление пользовательской функции div2
Добавьте следующий код в файлfunctions.js или functions.ts (в зависимости от того, использовались ли вы JavaScript или TypeScript).
/**
* Divides two numbers using batching
* @CustomFunction
* @param dividend The number being divided
* @param divisor The number the dividend is divided by
* @returns The result of dividing the two numbers
*/
function div2(dividend, divisor) {
return _pushOperation("div2", [dividend, divisor]);
}
Добавление глобальных переменных для отслеживания пакетных запросов
Затем добавьте две глобальные переменные в файлfunctions.js или functions.ts . _isBatchedRequestScheduled
важно позже для синхронизации пакетных вызовов к удаленной службе.
let _batch = [];
let _isBatchedRequestScheduled = false;
_pushOperation
Добавление функции
Когда Excel вызывает пользовательскую функцию, необходимо отправить операцию в пакетный массив. В следующем _pushOperation коде функции показано, как добавить новую операцию из пользовательской функции. Здесь создается новый элемент пакета, новое обещание для выполнения или отклонения операции, и элемент вставляется в пакетный массив.
В данном коде также проверяется, является ли пакет плановым. В этом примере выполнение пакете планируется каждые 100 мс. При необходимости этот интервал можно изменить. Чем значение выше, тем больше размер пакета, отправляемого в удаленную службу, и тем дольше пользователь должен ждать результатов. При низком значении в удаленную службу отправляется больше пакетов, но зато время ожидания снижается.
Функция создает объект invocationEntry , содержащий строковое имя выполняемой операции. Например, если у вас две пользовательские функции с именами multiply
и divide
, их можно использовать как имена операции в элементах пакета. args
содержит аргументы, которые были переданы пользовательской функции из Excel. И, наконец, resolve
методы или reject
хранят обещание с информацией, возвращаемой удаленной службой.
Добавьте следующий код в файлfunctions.js или functions.ts .
// This function encloses your custom functions as individual entries,
// which have some additional properties so you can keep track of whether or not
// a request has been resolved or rejected.
function _pushOperation(op, args) {
// Create an entry for your custom function.
console.log("pushOperation");
const invocationEntry = {
operation: op, // e.g., sum
args: args,
resolve: undefined,
reject: undefined,
};
// Create a unique promise for this invocation,
// and save its resolve and reject functions into the invocation entry.
const promise = new Promise((resolve, reject) => {
invocationEntry.resolve = resolve;
invocationEntry.reject = reject;
});
// Push the invocation entry into the next batch.
_batch.push(invocationEntry);
// If a remote request hasn't been scheduled yet,
// schedule it after a certain timeout, e.g., 100 ms.
if (!_isBatchedRequestScheduled) {
console.log("schedule remote request");
_isBatchedRequestScheduled = true;
setTimeout(_makeRemoteRequest, 100);
}
// Return the promise for this invocation.
return promise;
}
Проведение удаленного запроса
Цель функции _makeRemoteRequest
– передать пакет операций в удаленную службу, а затем возвратить результаты в каждую пользовательскую функцию. Сначала она создает копию пакетного массива. Это позволит сразу же начинать включение параллельных вызовов пользовательской функции из Excel в новый массив. Затем копия преобразуется в более простой массив, который не содержит информацию обещания. Не имеет смысла передавать обещания в удаленную службу, так как они не будут работать. Метод _makeRemoteRequest
будет отклонять или выполнять каждое обещание в зависимости от того, что возвратит удаленная служба.
Добавьте следующий код в файлfunctions.js или functions.ts .
// This is a private helper function, used only within your custom function add-in.
// You wouldn't call _makeRemoteRequest in Excel, for example.
// This function makes a request for remote processing of the whole batch,
// and matches the response batch to the request batch.
function _makeRemoteRequest() {
// Copy the shared batch and allow the building of a new batch while you are waiting for a response.
// Note the use of "splice" rather than "slice", which will modify the original _batch array
// to empty it out.
try{
console.log("makeRemoteRequest");
const batchCopy = _batch.splice(0, _batch.length);
_isBatchedRequestScheduled = false;
// Build a simpler request batch that only contains the arguments for each invocation.
const requestBatch = batchCopy.map((item) => {
return { operation: item.operation, args: item.args };
});
console.log("makeRemoteRequest2");
// Make the remote request.
_fetchFromRemoteService(requestBatch)
.then((responseBatch) => {
console.log("responseBatch in fetchFromRemoteService");
// Match each value from the response batch to its corresponding invocation entry from the request batch,
// and resolve the invocation promise with its corresponding response value.
responseBatch.forEach((response, index) => {
if (response.error) {
batchCopy[index].reject(new Error(response.error));
console.log("rejecting promise");
} else {
console.log("fulfilling promise");
console.log(response);
batchCopy[index].resolve(response.result);
}
});
});
console.log("makeRemoteRequest3");
} catch (error) {
console.log("error name:" + error.name);
console.log("error message:" + error.message);
console.log(error);
}
}
Переделка _makeRemoteRequest
для вашего собственного решения
Функция _makeRemoteRequest
вызывает метод _fetchFromRemoteService
, который, как будет видно позже, всего лишь имитирует удаленную службу. Это упрощает изучение и выполнение кода в данной статье. Но если вы хотите использовать этот код для фактической удаленной службы, следует внести следующие изменения.
- Выберите способ сериализации пакетных операций по сети. Например может потребоваться поместить массива в текст JSON.
- Вместо вызова
_fetchFromRemoteService
следует сделать сетевой вызов удаленной службы с передачей пакета операций.
Обработка пакетного вызова в удаленной службе
Последний шаг – это выполнение пакетного вызова в удаленной службе. В следующем примере кода показана функция _fetchFromRemoteService
. Эта функция распаковывает каждую операцию, выполняет указанную операцию и возвращает результат. Для учебных целей в данной статье применяется функция _fetchFromRemoteService
, которая запускается в вашей веб-надстройке и имитирует удаленную службу. Этот код можно добавить в файлfunctions.js или functions.ts , чтобы изучить и выполнить весь код, приведенный в этой статье, без необходимости настраивать реальную удаленную службу.
Добавьте следующий код в файлfunctions.js или functions.ts .
// This function simulates the work of a remote service. Because each service
// differs, you will need to modify this function appropriately to work with the service you are using.
// This function takes a batch of argument sets and returns a promise that may contain a batch of values.
// NOTE: When implementing this function on a server, also apply an appropriate authentication mechanism
// to ensure only the correct callers can access it.
async function _fetchFromRemoteService(requestBatch) {
// Simulate a slow network request to the server.
console.log("_fetchFromRemoteService");
await pause(1000);
console.log("postpause");
return requestBatch.map((request) => {
console.log("requestBatch server side");
const { operation, args } = request;
try {
if (operation === "div2") {
// Divide the first argument by the second argument.
return {
result: args[0] / args[1]
};
} else if (operation === "mul2") {
// Multiply the arguments for the given entry.
const myResult = args[0] * args[1];
console.log(myResult);
return {
result: myResult
};
} else {
return {
error: `Operation not supported: ${operation}`
};
}
} catch (error) {
return {
error: `Operation failed: ${operation}`
};
}
});
}
function pause(ms) {
console.log("pause");
return new Promise((resolve) => setTimeout(resolve, ms));
}
Переделка _fetchFromRemoteService
для действующей удаленной службы
Чтобы изменить функцию _fetchFromRemoteService
для запуска в динамической удаленной службе, внесите следующие изменения.
- В зависимости от платформы используемого сервера (Node.js или другая) сопоставьте сетевой вызов клиента с этой функцией.
- Удалите функцию
pause
, которая имитирует задержку в сети. - Измените объявление функции так, чтобы она работала с переданным параметром, если параметр изменяется для целей сети. Например, это может быть не массив а текст JSON, содержащий требуемые пакетные операции.
- Переделайте функцию для выполнения операций (или вызова функций, которые выполняют операции).
- Примените подходящий механизм проверки подлинности. Убедитесь, что доступ к функции есть только у предусмотренных вами вызывающих пользователей.
- Поместите код в удаленную службу.
Дальнейшие действия
Узнайте о различных параметрах, которые можно использовать в пользовательских функциях. Или узнайте, что лежит в основе веб-вызова через пользовательскую функцию.
Дополнительные ресурсы
Office Add-ins