Compartilhar via


Otimização de desempenho usando a API JavaScript do Excel

Escreva suplementos do Excel mais rápidos e dimensionáveis ao minimizar processos, criar batches e reduzir o tamanho do payload. Este artigo mostra padrões, anti-padrões e exemplos de código para o ajudar a otimizar operações comuns.

Melhorias rápidas

Aplique estas estratégias primeiro para o maior impacto imediato.

  • Batch loads and writes: group property load calls e, em seguida, make a single context.sync().
  • Minimizar a criação de objetos: operar em intervalos de blocos em vez de muitos intervalos de célula única.
  • Escreva dados em matrizes e, em seguida, atribua uma vez a um intervalo de destino.
  • Suspenda a atualização do ecrã ou o cálculo apenas em torno de grandes alterações.
  • Evite ciclos por iteração Excel.run ou context.sync() dentro.
  • Reutilizar objetos de folha de cálculo, tabela e intervalo em vez de voltar a consultar dentro de ciclos.
  • Mantenha os payloads abaixo dos limites de tamanho ao segmentar ou agregar antes da atribuição.

Importante

Muitos problemas de desempenho podem ser resolvidos através da utilização recomendada de load e sync chamadas. Veja a secção "Melhorias de desempenho com as APIs específicas da aplicação" de Limites de recursos e otimização de desempenho para Suplementos do Office para obter conselhos sobre como trabalhar com as APIs específicas da aplicação de forma eficiente.

Suspender temporariamente os processos do Excel

O Excel executa tarefas em segundo plano que reagem às ações de entrada e suplemento do utilizador. Colocar em pausa os processos selecionados pode melhorar o desempenho de operações grandes.

Suspender os cálculos temporariamente

Se precisar de atualizar um intervalo grande (como atribuir valores e, em seguida, recalcular fórmulas dependentes) e os resultados provisórios do recálculo não forem necessários, suspenda o cálculo temporariamente até ao próximo context.sync().

Ver a documentação de referência objeto de aplicativo para saber mais sobre como usar a APIsuspendApiCalculationUntilNextSync()para suspender e reativar cálculos de maneira muito fácil. O código seguinte demonstra como suspender temporariamente o cálculo.

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);
});

Apenas os cálculos de fórmulas são suspensos. Todas as referências alteradas ainda são reconstruídas. Por exemplo, mudar o nome de uma folha de cálculo ainda atualiza quaisquer referências em fórmulas para essa folha de cálculo.

Suspender a atualização da tela

O Excel apresenta as alterações à medida que ocorrem. Para atualizações grandes e iterativas, suprima as atualizações de ecrã intermédias. Application.suspendScreenUpdatingUntilNextSync() coloca em pausa as atualizações visuais até ao próximo context.sync() ou ao fim de Excel.run. Forneça feedback aos seus utilizadores, como status texto ou uma barra de progresso, porque a IU aparece inativa durante a suspensão.

Observação

Não chame suspendScreenUpdatingUntilNextSync repetidamente (por exemplo, num ciclo). As chamadas repetidas farão com que a janela do Excel cintile.

Habilitar e desabilitar eventos

Por vezes, pode melhorar o desempenho ao desativar eventos. Um exemplo de código mostrando como habilitar e desabilitar os eventos está no artigo trabalhar com eventos.

Importar dados em tabelas

Quando importa conjuntos de dados grandes diretamente para uma Tabela, como chamar TableRowCollection.add()repetidamente , o desempenho pode degradar-se. Em vez disso, siga a seguinte abordagem:

  1. Escreva toda a matriz 2D num intervalo com range.values.
  2. Crie a tabela através desse intervalo preenchido (worksheet.tables.add()).

Para tabelas existentes, defina valores table.getDataBodyRange() em massa. A tabela expande-se automaticamente.

Aqui está um exemplo dessa abordagem:

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();
});

Observação

Você pode converter convenientemente um objeto de tabela em um objeto de intervalo usando o métodoTable.convertToRange().

Melhores práticas de limite de tamanho de payload

A API JavaScript do Excel tem limitações de tamanho para chamadas à API. Excel na Web limita pedidos e respostas a 5 MB. A API devolve um RichAPI.Error erro se este limite for excedido. Em todas as plataformas, um intervalo está limitado a cinco milhões de células para obter operações. Os intervalos grandes excedem frequentemente ambos os limites.

O tamanho do payload de um pedido combina:

  • O número de chamadas à API.
  • O número de objetos, como Range objetos.
  • O comprimento do valor a definir ou obter.

Se receber RequestPayloadSizeLimitExceeded, aplique as seguintes estratégias para reduzir o tamanho antes de dividir as operações.

Estratégia 1: Mover valores inalterados para fora dos ciclos

Limite os processos dentro dos ciclos para melhorar o desempenho. No seguinte exemplo de código, context.workbook.worksheets.getActiveWorksheet() pode ser movido para fora do for ciclo porque não é alterado nesse ciclo.

// 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();
  });
}

O seguinte exemplo de código mostra uma lógica semelhante, mas com uma estratégia melhorada. O valor context.workbook.worksheets.getActiveWorksheet() é obtido antes do ciclo porque não é alterado. Apenas os valores que variam devem ser obtidos dentro do ciclo.

// 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();
  });
}

Estratégia 2: Criar menos objetos de intervalo

Crie menos objetos de intervalo para melhorar o desempenho e reduzir o tamanho do payload. Seguem-se duas abordagens.

Dividir cada matriz de intervalo em múltiplas matrizes

Uma forma de criar menos objetos de intervalo é dividir cada matriz de intervalo em múltiplas matrizes e, em seguida, processar cada nova matriz com um ciclo e uma nova context.sync() chamada.

Importante

Utilize esta estratégia apenas depois de confirmar que excedeu o limite de tamanho do payload. Vários ciclos reduzem o tamanho de cada pedido de payload, mas também adicionam chamadas adicionais context.sync() e podem prejudicar o desempenho.

O seguinte exemplo de código tenta processar uma grande matriz de intervalos num único ciclo e, em seguida, uma única context.sync() chamada. Processar demasiados valores de intervalo numa context.sync() chamada faz com que o tamanho do pedido de payload exceda o limite de 5 MB.

// 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(); 
  });
}

O seguinte exemplo de código mostra uma lógica semelhante ao exemplo de código anterior, mas com uma estratégia que evita exceder o limite de tamanho do pedido de payload de 5 MB. No seguinte exemplo de código, os intervalos são processados em dois ciclos separados e cada ciclo é seguido de uma context.sync() chamada.

// 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(); 
  });
}

Definir valores de intervalo numa matriz

Outra forma de criar menos objetos de intervalo é criar uma matriz, utilizar um ciclo para definir todos os dados nessa matriz e, em seguida, passar os valores da matriz para um intervalo. Isto beneficia o desempenho e o tamanho do payload. Em vez de chamar range.values cada intervalo num ciclo, range.values é chamado uma vez fora do ciclo.

O seguinte exemplo de código mostra como criar uma matriz, definir os valores dessa matriz num for ciclo e, em seguida, passar os valores da matriz para um intervalo fora do ciclo.

// 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();
  });
}

Próximas etapas

Confira também