Заметка
Доступ к этой странице требует авторизации. Вы можете попробовать войти в систему или изменить каталог.
Доступ к этой странице требует авторизации. Вы можете попробовать сменить директорию.
Создавайте более быстрые и масштабируемые надстройки Excel, минимизируя процессы, функции пакетной обработки и уменьшая размер полезных данных. В этой статье показаны шаблоны, антишаблоны и примеры кода, которые помогут оптимизировать распространенные операции.
Быстрые улучшения
Сначала применяйте эти стратегии для наибольшего немедленного воздействия.
- Пакетная загрузка и запись: вызовы свойств
loadгруппы, а затем сделайте один .context.sync() - Минимизируйте создание объектов: используйте диапазоны блоков, а не несколько диапазонов с одной ячейкой.
- Записывайте данные в массивы, а затем назначьте один раз целевому диапазону.
- Приостановите обновление экрана или вычисление только в соответствии с большими изменениями.
- Избегайте итерации
Excel.runилиcontext.sync()внутри циклов. - Повторно используйте объекты листа, таблицы и диапазона вместо повторного запроса внутри циклов.
- Поддерживайте полезные данные ниже ограничений по размеру, блокируя или агрегируя перед назначением.
Важно!
Многие проблемы с производительностью можно устранить с помощью рекомендуемого load использования и sync вызовов. Рекомендации по эффективной работе с API-интерфейсами приложений см. в разделе "Улучшения производительности с помощью API для конкретного приложения" статьи Ограничения ресурсов и оптимизация производительности для надстроек Office .
Временная приостановка процессов Excel
Excel выполняет фоновые задачи, которые реагируют на входные данные пользователя и действия надстроек. Приостановка выбранных процессов может повысить производительность крупных операций.
Временная приостановка вычисления
Если необходимо обновить большой диапазон (например, для назначения значений и последующего пересчета зависимых формул), а промежуточные результаты пересчета не требуются, временно приостановите вычисление до следующего context.sync().
Дополнительные сведения об использовании API suspendApiCalculationUntilNextSync() для приостановки и повторного включения вычислений удобным способом см. в справочном документе Объект Application. В следующем коде показано, как временно приостановить вычисление.
await Excel.run(async (context) => {
let app = context.workbook.application;
let sheet = context.workbook.worksheets.getItem("sheet1");
let rangeToSet: Excel.Range;
let rangeToGet: Excel.Range;
app.load("calculationMode");
await context.sync();
// Calculation mode should be "Automatic" by default
console.log(app.calculationMode);
rangeToSet = sheet.getRange("A1:C1");
rangeToSet.values = [[1, 2, "=SUM(A1:B1)"]];
rangeToGet = sheet.getRange("A1:C1");
rangeToGet.load("values");
await context.sync();
// Range value should be [1, 2, 3] now
console.log(rangeToGet.values);
// Suspending recalculation
app.suspendApiCalculationUntilNextSync();
rangeToSet = sheet.getRange("A1:B1");
rangeToSet.values = [[10, 20]];
rangeToGet = sheet.getRange("A1:C1");
rangeToGet.load("values");
app.load("calculationMode");
await context.sync();
// Range value should be [10, 20, 3] when we load the property, because calculation is suspended at that point
console.log(rangeToGet.values);
// Calculation mode should still be "Automatic" even with suspend recalculation
console.log(app.calculationMode);
rangeToGet.load("values");
await context.sync();
// Range value should be [10, 20, 30] when we load the property, because calculation is resumed after last sync
console.log(rangeToGet.values);
});
Приостанавливаются только вычисления формул. Все измененные ссылки по-прежнему перестраиваются. Например, переименование листа по-прежнему обновляет все ссылки в формулах на этот лист.
Приостановка обновления экрана
Excel отображает изменения по мере их возникновения. Для больших итеративных обновлений подавляйте промежуточные обновления экрана.
Application.suspendScreenUpdatingUntilNextSync()приостанавливает обновления визуальных элементов до следующего context.sync() или конца .Excel.run Предоставьте пользователям отзывы, например текст состояния или индикатор выполнения, так как пользовательский интерфейс неактивный во время приостановки.
Примечание.
Не вызывайте suspendScreenUpdatingUntilNextSync повторно (например, в цикле). При повторных вызовах окно Excel будет мерцать.
Включение и отключение событий
Иногда можно повысить производительность, отключив события. Пример кода, в котором показано, как включить и отключить события, см. в статье Работа с событиями.
Импорт данных в таблицы
При импорте больших наборов данных непосредственно в таблицу, например при многократном вызове TableRowCollection.add(), производительность может снизиться. Вместо этого используйте следующий подход:
- Запишите весь массив 2D в диапазон с помощью
range.values. - Создайте таблицу над заполненным диапазоном (
worksheet.tables.add()).
Для существующих таблиц задайте значения в пакетном режиме table.getDataBodyRange() . Таблица разворачивается автоматически.
Ниже приведен пример такого способа.
await Excel.run(async (context) => {
let sheet = context.workbook.worksheets.getItem("Sheet1");
// Write the data into the range first.
let range = sheet.getRange("A1:B3");
range.values = [["Key", "Value"], ["A", 1], ["B", 2]];
// Create the table over the range
let table = sheet.tables.add('A1:B3', true);
table.name = "Example";
await context.sync();
// Insert a new row to the table
table.getDataBodyRange().getRowsBelow(1).values = [["C", 3]];
// Change a existing row value
table.getDataBodyRange().getRow(1).values = [["D", 4]];
await context.sync();
});
Примечание.
Можно легко преобразовать объект Table в объект Range, используя метод Table.convertToRange().
Рекомендации по ограничению размера полезных данных
Api JavaScript для Excel имеет ограничения по размеру для вызовов API.
Excel в Интернете ограничивает количество запросов и ответов до 5 МБ. API возвращает ошибку, RichAPI.Error если это ограничение превышено. На всех платформах диапазон ограничен пятью миллионами ячеек для операций получения. Большие диапазоны часто превышают оба ограничения.
Размер полезных данных запроса объединяет:
- Количество вызовов API.
- Количество объектов, таких как
Rangeобъекты. - Длина устанавливаемого или получаемого значения.
Если вы получаете RequestPayloadSizeLimitExceeded, примените следующие стратегии для уменьшения размера перед операциями разделения.
Стратегия 1. Перемещение без изменений значений из циклов
Ограничьте процессы внутри циклов, чтобы повысить производительность. В следующем примере кода можно переместить из for цикла, context.workbook.worksheets.getActiveWorksheet() так как он не изменяется в этом цикле.
// DO NOT USE THIS CODE SAMPLE. This sample shows a poor performance strategy.
async function run() {
await Excel.run(async (context) => {
let ranges = [];
// This sample retrieves the worksheet every time the loop runs, which is bad for performance.
for (let i = 0; i < 7500; i++) {
let rangeByIndex = context.workbook.worksheets.getActiveWorksheet().getRangeByIndexes(i, 1, 1, 1);
}
await context.sync();
});
}
В следующем примере кода показана аналогичная логика, но с улучшенной стратегией. Значение context.workbook.worksheets.getActiveWorksheet() извлекается перед циклом, так как оно не изменяется. В цикле должны извлекаться только разные значения.
// This code sample shows a good performance strategy.
async function run() {
await Excel.run(async (context) => {
let ranges = [];
// Retrieve the worksheet outside the loop.
let worksheet = context.workbook.worksheets.getActiveWorksheet();
// Only process the necessary values inside the loop.
for (let i = 0; i < 7500; i++) {
let rangeByIndex = worksheet.getRangeByIndexes(i, 1, 1, 1);
}
await context.sync();
});
}
Стратегия 2. Создание меньшего количества объектов диапазона
Создание меньшего количества объектов диапазона для повышения производительности и уменьшения размера полезных данных. Следуйте двум подходам.
Разделение каждого массива диапазона на несколько массивов
Один из способов создания меньшего количества объектов диапазона — разделить каждый массив диапазона на несколько массивов, а затем обработать каждый новый массив с помощью цикла и нового context.sync() вызова.
Важно!
Используйте эту стратегию только после подтверждения превышения предельного размера полезных данных. Несколько циклов уменьшают размер каждого запроса полезных данных, но также добавляют дополнительные context.sync() вызовы и могут снизить производительность.
Следующий пример кода пытается обработать большой массив диапазонов в одном цикле, а затем один context.sync() вызов. Обработка слишком большого количества значений диапазона в одном context.sync() вызове приводит к тому, что размер запроса полезных данных превысит ограничение в 5 МБ.
// This code sample does not show a recommended strategy.
// Calling 10,000 rows would likely exceed the 5MB payload size limit in a real-world situation.
async function run() {
await Excel.run(async (context) => {
let worksheet = context.workbook.worksheets.getActiveWorksheet();
// This sample attempts to process too many ranges at once.
for (let row = 1; row < 10000; row++) {
let range = sheet.getRangeByIndexes(row, 1, 1, 1);
range.values = [["1"]];
}
await context.sync();
});
}
В следующем примере кода показана логика, аналогичная предыдущему примеру кода, но со стратегией, которая позволяет избежать превышения предельного размера запроса полезных данных в 5 МБ. В следующем примере кода диапазоны обрабатываются в два отдельных цикла, за каждым из которых следует context.sync() вызов.
// This code sample shows a strategy for reducing payload request size.
// However, using multiple loops and `context.sync()` calls negatively impacts performance.
// Only use this strategy if you've determined that you're exceeding the payload request limit.
async function run() {
await Excel.run(async (context) => {
let worksheet = context.workbook.worksheets.getActiveWorksheet();
// Split the ranges into two loops, rows 1-5000 and then 5001-10000.
for (let row = 1; row < 5000; row++) {
let range = worksheet.getRangeByIndexes(row, 1, 1, 1);
range.values = [["1"]];
}
// Sync after each loop.
await context.sync();
for (let row = 5001; row < 10000; row++) {
let range = worksheet.getRangeByIndexes(row, 1, 1, 1);
range.values = [["1"]];
}
await context.sync();
});
}
Установка значений диапазона в массиве
Другим способом создания меньшего количества объектов диапазона является создание массива, использование цикла для задания всех данных в этом массиве, а затем передача значений массива в диапазон. Это дает преимущества как производительности, так и размера полезных данных. Вместо вызова range.values для каждого диапазона в цикле range.values вызывается один раз за пределами цикла.
В следующем примере кода показано, как создать массив, задать значения этого массива в for цикле, а затем передать значения массива в диапазон за пределами цикла.
// This code sample shows a good performance strategy.
async function run() {
await Excel.run(async (context) => {
const worksheet = context.workbook.worksheets.getActiveWorksheet();
// Create an array.
const array = new Array(10000);
// Set the values of the array inside the loop.
for (let i = 0; i < 10000; i++) {
array[i] = [1];
}
// Pass the array values to a range outside the loop.
let range = worksheet.getRange("A1:A10000");
range.values = array;
await context.sync();
});
}
Дальнейшие действия
- Ознакомьтесь с ограничениями на уровне узла и оптимизацией производительности ресурсов.
- Изучите работу с несколькими диапазонами , чтобы создать меньше объектов.
- Добавьте данные телеметрии для таких данных, как длительность операций и количество строк, чтобы обеспечить дальнейшую оптимизацию производительности.