Partekatu honen bidez:


Optimización del rendimiento con la API de JavaScript de Excel

Escriba complementos de Excel más rápidos y escalables minimizando los procesos, las funciones de procesamiento por lotes y reduciendo el tamaño de carga. En este artículo se muestran patrones, antipatrones y ejemplos de código para ayudarle a optimizar las operaciones comunes.

Mejoras rápidas

Aplique estas estrategias primero para el mayor impacto inmediato.

  • Batch carga y escribe: llamadas a propiedades load de grupo y, a continuación, crea un único context.sync().
  • Minimizar la creación de objetos: funciona en intervalos de bloques en lugar de en muchos intervalos de celdas únicas.
  • Escriba datos en matrices y, a continuación, asígnelos una vez a un intervalo de destino.
  • Suspenda la actualización de pantalla o el cálculo solo en torno a grandes cambios.
  • Evite los bucles por iteración Excel.run o context.sync() dentro.
  • Reutilice objetos de hoja de cálculo, tabla y rango en lugar de volver a consultar dentro de bucles.
  • Mantenga las cargas por debajo de los límites de tamaño mediante la fragmentación o agregación antes de la asignación.

Importante

Muchos problemas de rendimiento se pueden solucionar mediante el uso recomendado de load llamadas y sync . Consulte la sección "Mejoras de rendimiento con las API específicas de la aplicación" de Límites de recursos y optimización del rendimiento para complementos de Office para obtener consejos sobre cómo trabajar con las API específicas de la aplicación de forma eficaz.

Suspender temporalmente los procesos de Excel

Excel realiza tareas en segundo plano que reaccionan a las acciones de entrada y complemento del usuario. Pausar los procesos seleccionados puede mejorar el rendimiento de las operaciones de gran tamaño.

Suspender el cálculo temporalmente

Si necesita actualizar un intervalo grande (por ejemplo, para asignar valores y, a continuación, recalcular fórmulas dependientes) y no se necesitan resultados de actualización provisionales, suspenda el cálculo temporalmente hasta el siguiente context.sync().

Vea la documentación de referencia de Objeto Application para obtener información sobre cómo usar la API de suspendApiCalculationUntilNextSync() para suspender y reactivar cálculos de forma práctica. En el código siguiente se muestra cómo suspender el cálculo temporalmente.

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

Solo se suspenden los cálculos de fórmulas. Las referencias modificadas se siguen reconstruyendo. Por ejemplo, al cambiar el nombre de una hoja de cálculo, se siguen actualizando las referencias de las fórmulas a esa hoja de cálculo.

Suspender la actualización de la pantalla

Excel muestra los cambios a medida que se producen. En el caso de las actualizaciones grandes e iterativas, suprima las actualizaciones de pantalla intermedias. Application.suspendScreenUpdatingUntilNextSync() pausa las actualizaciones visuales hasta el siguiente context.sync() o el final de Excel.run. Proporcione a los usuarios comentarios, como texto de estado o una barra de progreso, porque la interfaz de usuario aparece inactiva durante la suspensión.

Nota:

No llame a suspendScreenUpdatingUntilNextSync repetidamente (por ejemplo, en un bucle). Las llamadas repetidas harán que la ventana de Excel parpade.

Habilitar y deshabilitar eventos

A veces, puede mejorar el rendimiento deshabilitando eventos. Para ver un código de ejemplo que muestra cómo habilitar y deshabilitar los eventos, vaya al artículo Trabajar con eventos.

Importar datos en tablas

Al importar conjuntos de datos grandes directamente en una tabla, como llamar TableRowCollection.add()repetidamente a , el rendimiento puede degradarse. En su lugar, adopte el siguiente enfoque:

  1. Escriba toda la matriz 2D en un intervalo con range.values.
  2. Cree la tabla sobre ese intervalo rellenado (worksheet.tables.add()).

En el caso de las tablas existentes, establezca los valores en table.getDataBodyRange() de forma masiva. La tabla se expande automáticamente.

Aquí tiene un ejemplo de este enfoque:

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

Nota:

Puede convertir fácilmente un objeto Table en un objeto de intervalo utilizando el método Table.convertToRange().

Procedimientos recomendados de límite de tamaño de carga

La API de JavaScript de Excel tiene limitaciones de tamaño para las llamadas API. Excel en la Web limita las solicitudes y respuestas a 5 MB. La API devuelve un RichAPI.Error error si se supera este límite. En todas las plataformas, un rango está limitado a cinco millones de celdas para obtener operaciones. Los intervalos grandes suelen superar ambos límites.

El tamaño de carga de una solicitud combina:

  • Número de llamadas API.
  • Número de objetos, como Range objetos.
  • Longitud del valor que se va a establecer o obtener.

Si obtiene RequestPayloadSizeLimitExceeded, aplique las siguientes estrategias para reducir el tamaño antes de dividir las operaciones.

Estrategia 1: Mover los valores sin cambios fuera de bucles

Limite los procesos dentro de bucles para mejorar el rendimiento. En el ejemplo de código siguiente, context.workbook.worksheets.getActiveWorksheet() se puede mover fuera del for bucle porque no cambia dentro de ese bucle.

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

En el ejemplo de código siguiente se muestra una lógica similar, pero con una estrategia mejorada. El valor context.workbook.worksheets.getActiveWorksheet() se recupera antes del bucle porque no cambia. Solo los valores que varían deben recuperarse dentro del bucle.

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

Estrategia 2: Crear menos objetos de intervalo

Cree menos objetos de intervalo para mejorar el rendimiento y reducir el tamaño de carga. A continuación se indican dos enfoques.

Dividir cada matriz de rango en varias matrices

Una manera de crear menos objetos de rango es dividir cada matriz de rango en varias matrices y, a continuación, procesar cada nueva matriz con un bucle y una nueva context.sync() llamada.

Importante

Use esta estrategia solo después de confirmar que supera el límite de tamaño de carga. Varios bucles reducen el tamaño de cada solicitud de carga, pero también agregan llamadas adicionales context.sync() y pueden afectar al rendimiento.

El ejemplo de código siguiente intenta procesar una gran matriz de intervalos en un único bucle y, a continuación, una sola context.sync() llamada. El procesamiento de demasiados valores de intervalo en una context.sync() llamada hace que el tamaño de la solicitud de carga supere el límite 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(); 
  });
}

En el ejemplo de código siguiente se muestra una lógica similar al ejemplo de código anterior, pero con una estrategia que evita superar el límite de tamaño de solicitud de carga de 5 MB. En el ejemplo de código siguiente, los intervalos se procesan en dos bucles independientes y cada bucle va seguido de una context.sync() llamada.

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

Establecimiento de valores de rango en una matriz

Otra manera de crear menos objetos de rango es crear una matriz, usar un bucle para establecer todos los datos de esa matriz y, a continuación, pasar los valores de matriz a un rango. Esto beneficia tanto el rendimiento como el tamaño de carga. En lugar de llamar a range.values para cada intervalo de un bucle, range.values se llama una vez fuera del bucle.

En el ejemplo de código siguiente se muestra cómo crear una matriz, establecer los valores de esa matriz en un for bucle y, a continuación, pasar los valores de matriz a un intervalo fuera del bucle.

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

Pasos siguientes

Vea también