Optimización del rendimiento con la API de JavaScript de ExcelPerformance optimization using the Excel JavaScript API

Hay varias maneras de realizar tareas comunes con la API de JavaScript de Excel. Encontrará diferencias importantes en el rendimiento entre varios métodos. En este artículo encontrará instrucciones y ejemplos de código que le muestran cómo realizar tareas comunes de forma eficaz con la API de JavaScript de Excel.There are multiple ways that you can perform common tasks with the Excel JavaScript API. You'll find significant performance differences between various approaches. This article provides guidance and code samples to show you how to perform common tasks efficiently using Excel JavaScript API.

Minimizar el número de llamadas sync()Minimize the number of sync() calls

En la API de JavaScript de Excel, sync() es la única operación asincrónica y puede ser lenta en determinadas circunstancias, especialmente para Excel en la web. Para optimizar el rendimiento, se recomienda minimizar el número de llamadas a sync() y poner a la cola todos los cambios posibles antes de llamar.In the Excel JavaScript API, sync() is the only asynchronous operation, and it can be slow under some circumstances, especially for Excel on the web. To optimize performance, minimize the number of calls to sync() by queueing up as many changes as possible before calling it.

Consulte Conceptos básicos: sync() para obtener ejemplos de código que siguen esta práctica.See Core Concepts - sync() for code samples that follow this practice.

Minimizar la cantidad de objetos proxy creadosMinimize the number of proxy objects created

Evite crear varias veces al mismo objeto proxy. En su lugar, si necesita el mismo objeto proxy para más de una operación, créelo una vez y asígneselo a una variable; use esa variable en el código.Avoid repeatedly creating the same proxy object. Instead, if you need the same proxy object for more than one operation, create it once and assign it to a variable, then use that variable in your code.

// BAD: repeated calls to .getRange() to create the same proxy object
worksheet.getRange("A1").format.fill.color = "red";
worksheet.getRange("A1").numberFormat = "0.00%";
worksheet.getRange("A1").values = [[1]];

// GOOD: create the range proxy object once and assign to a variable
var range = worksheet.getRange("A1")
range.format.fill.color = "red";
range.numberFormat = "0.00%";
range.values = [[1]];

// ALSO GOOD: use a "set" method to immediately set all the properties without even needing to create a variable!
worksheet.getRange("A1").set({
    numberFormat: [["0.00%"]],
    values: [[1]],
    format: {
        fill: {
            color: "red"
        }
    }
});

Cargue solo las propiedades necesariasLoad necessary properties only

En la API de JavaScript de Excel, deberá cargar explícitamente las propiedades de un objeto proxy. Aunque puede cargar todas las propiedades a la vez con una llamada vacía a load(), esta acción puede afectar al rendimiento de forma significativa. En su lugar, le recomendamos cargar solo las propiedades necesarias, especialmente para los objetos que tienen un gran número de propiedades.In the Excel JavaScript API, you need to explicitly load the properties of a proxy object. Although you're able to load all the properties at once with an empty load() call, that approach can have significant performance overhead. Instead, we suggest that you only load the necessary properties, especially for those objects which have a large number of properties.

Por ejemplo, si solo pretende leer la address propiedad de un objeto Range, especifique sólo esa propiedad cuando llame al load() método:For example, if you only intend to read the address property of a range object, specify only that property when you call the load() method:

range.load('address');

Puede llamar al load() método de cualquiera de las maneras siguientes:You can call load() method in any of the following ways:

Sintaxis:Syntax:

object.load(string: properties);
// or
object.load(array: properties);
// or
object.load({ loadOption });

Dónde:Where:

  • propertieses la lista de las propiedades que se van a cargar, especificadas como cadenas delimitadas por comas o como una matriz de nombres. Para obtener más información, vea los load() métodos definidos para los objetos en referencia de la API de JavaScript de Excel.properties is the list of properties to load, specified as comma-delimited strings or as an array of names. For more information, see the load() methods defined for objects in Excel JavaScript API reference.
  • loadOption especifica un objeto que describe las opciones selection, expansion, top y skip. Consulte las opciones de carga de objetos para obtener más detalles.loadOption specifies an object that describes the selection, expansion, top, and skip options. See object load options for details.

Tenga en cuenta que algunas de las "propiedades" de un objeto pueden tener el mismo nombre que otro objeto. Por ejemplo, format es una propiedad en objeto Range, pero format también es un objeto. Por lo tanto, si realiza una llamada como range.load("format") , esto equivale a range.format.load() , que es una llamada a Load () vacía que puede causar problemas de rendimiento, como se ha descrito anteriormente. Para evitar esto, el código solo debe cargar los "nodos hoja" en un árbol de objetos.Please be aware that some of the "properties" under an object may have the same name as another object. For example, format is a property under range object, but format itself is an object as well. So, if you make a call such as range.load("format"), this is equivalent to range.format.load(), which is an empty load() call that can cause performance problems as outlined previously. To avoid this, your code should only load the "leaf nodes" in an object tree.

Suspender temporalmente los procesos de ExcelSuspend Excel processes temporarily

Excel tiene una serie de tareas en segundo plano que reaccionan a las entradas de los usuarios y del complemento.Excel has a number of background tasks reacting to input from both users and your add-in. Algunos de estos procesos de Excel pueden controlarse para conducir a una mejora del rendimiento.Some of these Excel processes can be controlled to yield a performance benefit. Esto es especialmente útil cuando el complemento trabaja con grandes conjuntos de datos.This is especially helpful when your add-in deals with large data sets.

Suspender el cálculo temporalmenteSuspend calculation temporarily

Si intenta realizar una operación en un gran número de celdas (por ejemplo, establecer el valor de un objeto de intervalo enorme) y no le importa suspender el cálculo en Excel temporalmente mientras su operación se finaliza, le recomendamos que suspenda el cálculo hasta que se llame al siguiente context.sync().If you are trying to perform an operation on a large number of cells (for example, setting the value of a huge range object) and you don't mind suspending the calculation in Excel temporarily while your operation finishes, we recommend that you suspend calculation until the next context.sync() is called.

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.See the Application Object reference documentation for information about how to use the suspendApiCalculationUntilNextSync() API to suspend and reactivate calculations in a very convenient way. El código siguiente muestra cómo interrumpir el cálculo temporalmente:The following code demonstrates how to suspend calculation temporarily:

Excel.run(async function(ctx) {
    var app = ctx.workbook.application;
    var sheet = ctx.workbook.worksheets.getItem("sheet1");
    var rangeToSet: Excel.Range;
    var rangeToGet: Excel.Range;
    app.load("calculationMode");
    await ctx.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 ctx.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 ctx.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 ctx.sync();
    // Range value should be [10, 20, 30] when we load the property, because calculation is resumed after last sync
    console.log(rangeToGet.values);
})

Tenga en cuenta que solo se suspenden los cálculos de fórmulas.Please note that only formula calculations are suspended. Las referencias modificadas se siguen reconstruyendo.Any altered references are still rebuilt. Por ejemplo, cambiar el nombre de una hoja de cálculo sigue actualizando las referencias de las fórmulas de la hoja de cálculo.For example, renaming a worksheet still updates any references in formulas to that worksheet.

Suspender la actualización de la pantallaSuspend screen updating

Excel muestra los cambios realizados por el complemento casi al mismo tiempo que estos ocurren en el código.Excel displays changes your add-in makes approximately as they happen in the code. Para los conjuntos de datos iterativos de gran tamaño puede que no sea necesario ver el progreso de la pantalla en tiempo real.For large, iterative data sets, you may not need to see this progress on the screen in real-time. Application.suspendScreenUpdatingUntilNextSync() pausa las actualizaciones visuales de Excel hasta que el complemento llama a context.sync() o hasta que Excel.run termina (llamando a context.sync de manera implícita).Application.suspendScreenUpdatingUntilNextSync() pauses visual updates to Excel until the add-in calls context.sync(), or until Excel.run ends (implicitly calling context.sync). Tenga en cuenta que Excel no mostrará signos de actividad hasta la siguiente sincronización. El complemento debe dar instrucciones a los usuarios para prepararlos para este retraso o proporcionar una barra de estado para mostrar la actividad.Be aware, Excel will not show any signs of activity until the next sync. Your add-in should either give users guidance to prepare them for this delay or provide a status bar to demonstrate activity.

Nota

No llamar suspendScreenUpdatingUntilNextSync repetidamente (como en un bucle).Don't call suspendScreenUpdatingUntilNextSync repeatedly (such as in a loop). Las llamadas repetidas harán que la ventana de Excel parpadee.Repeated calls will cause the Excel window to flicker.

Habilitar y deshabilitar eventosEnable and disable events

El rendimiento de un complemento puede mejorar mediante la deshabilitación de eventos.Performance of an add-in may be improved by disabling events. Para ver un código de ejemplo que muestra cómo habilitar y deshabilitar los eventos, vaya al artículo Trabajar con eventos.A code sample showing how to enable and disable events is in the Work with Events article.

Actualizar todas las celdas de un rangoUpdate all cells in a range

Cuando necesite actualizar todas las celdas de un rango con el mismo valor o propiedad, la acción puede ser lenta si se hace a través de una matriz bidimensional que especifica varias veces el mismo valor, ya que este enfoque requiere que Excel recorra todas las celdas del rango para configurar cada individualmente. Excel tiene una forma más eficaz de actualizar todas las celdas de un rango con el mismo valor o propiedad.When you need to update all cells in a range with the same value or property, it can be slow to do this via a 2-dimensional array that repeatedly specifies the same value, since that approach requires Excel to iterate over all of the cells in the range to set each one separately. Excel has a more efficient way to update all the cells in a range with the same value or property.

Si necesita aplicar el mismo valor, el mismo formato de número o la misma fórmula en un rango de celdas, es más eficaz especificar un valor único en lugar de una matriz de valores. Si lo hace, el rendimiento mejorará significativamente. Para ver un código de ejemplo que muestra este enfoque en acción, consulte Conceptos básicos: actualizar todas las celdas de un rango.If you need to apply the same value, the same number format, or the same formula to a range of cells, it's more efficient to specify a single value instead of an array of values. Doing so will significantly improve performance. For a code sample that shows this approach in action, see Core concepts - Update all cells in a range.

Un escenario común donde puede aplicar este método es al establecer distintos formatos de número en diferentes columnas de una hoja de cálculo. En este caso, simplemente puede recorrer las columnas y establecer el formato de número en cada columna con un único valor. Trate cada columna como un rango, como se muestra en el código de ejemplo Actualizar todas las celdas de un rango.A common scenario where you can apply this approach is when setting different number formats on different columns in a worksheet. In this case, you can simply iterate through the columns and set the number format on each column with a single value. Handle each column as a range, as shown in the Update all cells in a range code sample.

Nota

Si usa TypeScript, verá un error de compilación que indica que no se puede establecer un valor único en una matriz bidimensional. Esto es inevitable, ya que los valores son una matriz 2D al recuperar las propiedades y TypeScript no permite diferentes establecedores vs. captadores. Sin embargo, una solución alternativa es establecer los valores con un sufijo as any, por ejemplo, range.values = "hello world" as any.If you're using TypeScript, you will notice a compile error saying that a single value cannot be set to a 2D array. This is unavoidable since the values are a 2D array when retrieving the properties, and TypeScript does not allow different setter vs getter types. However, a simple workaround is to set the values with a as any suffix, e.g., range.values = "hello world" as any.

Importar datos en tablasImporting data into tables

Al intentar importar una gran cantidad de datos directamente en un objeto Table directamente (por ejemplo, con TableRowCollection.add()), es posible que note un descenso del rendimiento. Si está intentando agregar una nueva tabla, debe rellenar los datos primero estableciendo range.values y, a continuación, llamar a worksheet.tables.add() para crear una tabla en el rango. Si prefiere escribir los datos en una tabla existente, escriba los datos en un objeto de intervalo mediante table.getDataBodyRange() y la tabla se expandirá automáticamente.When trying to import a huge amount of data directly into a Table object directly (for example, by using TableRowCollection.add()), you might experience slow performance. If you are trying to add a new table, you should fill in the data first by setting range.values, and then call worksheet.tables.add() to create a table over the range. If you are trying to write data into an existing table, write the data into a range object via table.getDataBodyRange(), and the table will expand automatically.

Aquí tiene un ejemplo de este enfoque:Here is an example of this approach:

Excel.run(async (ctx) => {
    var sheet = ctx.workbook.worksheets.getItem("Sheet1");
    // Write the data into the range first 
    var range = sheet.getRange("A1:B3");
    range.values = [["Key", "Value"], ["A", 1], ["B", 2]];

    // Create the table over the range
    var table = sheet.tables.add('A1:B3', true);
    table.name = "Example";
    await ctx.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 ctx.sync();
})

Nota

Puede convertir fácilmente un objeto Table en un objeto de intervalo utilizando el método Table.convertToRange().You can conveniently convert a Table object to a Range object by using the Table.convertToRange() method.

Dejar de seguir rangos innecesariosUntrack unneeded ranges

La capa de JavaScript crea objetos proxy para el complemento para interactuar con el libro de Excel y los intervalos subyacentes.The JavaScript layer creates proxy objects for your add-in to interact with the Excel workbook and underlying ranges. Estos objetos se conservan en la memoria hasta que se llama a context.sync().These objects persist in memory until context.sync() is called. Las operaciones por lotes de gran tamaño pueden generar una gran cantidad de objetos proxy que el complemento solo necesitan una vez y se pueden liberar de la memoria antes de ejecutar el lote.Large batch operations may generate a lot of proxy objects that are only needed once by the add-in and can be released from memory before the batch executes.

El método Range.untrack() libera un objeto range de Excel de la memoria.The Range.untrack() method releases an Excel Range object from memory. Llamar a este método una vez que el complemento termine con el rango debería suponer una mejora del rendimiento evidente al usar grandes cantidades de objetos range.Calling this method after your add-in is done with the range should yield a noticeable performance benefit when using large numbers of Range objects.

Nota

Range.untrack() es un método abreviado para ClientRequestContext.trackedObjects.remove(thisRange).Range.untrack() is a shortcut for ClientRequestContext.trackedObjects.remove(thisRange). Si desea dejar de seguir un objeto proxy, quítelo de la lista de objetos seguidos en el contexto.Any proxy object can be untracked by removing it from the tracked objects list in the context. Normalmente, los objetos range son los únicos de Excel que se usan en cantidad suficiente para poder justificar el cese del seguimiento.Typically, Range objects are the only Excel objects used in sufficient quantity to justify untracking.

El ejemplo de código siguiente rellena un rango seleccionado con datos. Las celdas se rellenan de una en una.The following code sample fills a selected range with data, one cell at a time. Después de agregar el valor a la celda, el rango que representa esa celda se deja de seguir.After the value is added to the cell, the range representing that cell is untracked. Ejecute este código con un rango seleccionado de 10 000 a 20 000 celdas, primero con la línea cell.untrack() y, a continuación, sin ella.Run this code with a selected range of 10,000 to 20,000 cells, first with the cell.untrack() line, and then without it. Notará que el código se ejecuta más rápido con la línea cell.untrack() que sin ella.You should notice the code runs faster with the cell.untrack() line than without it. También es posible que observe un tiempo de respuesta más rápido posteriormente, ya que el paso de limpieza requiere menos tiempo.You may also notice a quicker response time afterwards, since the cleanup step takes less time.

Excel.run(async (context) => {
    var largeRange = context.workbook.getSelectedRange();
    largeRange.load(["rowCount", "columnCount"]);
    await context.sync();

    for (var i = 0; i < largeRange.rowCount; i++) {
        for (var j = 0; j < largeRange.columnCount; j++) {
            var cell = largeRange.getCell(i, j);
            cell.values = [[i *j]];

            // call untrack() to release the range from memory
            cell.untrack();
        }
    }

    await context.sync();
});

Vea tambiénSee also