Compartir a través de


Programación asíncrona en los complementos de Office

Importante

Este artículo se aplica a las API comunes, el modelo de API de JavaScript de Office que se introdujo con Office 2013. Estas API incluyen características como la interfaz de usuario, los cuadros de diálogo y la configuración del cliente, que son comunes a varios tipos de aplicaciones de Office. Los complementos de Outlook usan exclusivamente API comunes, especialmente los subconjuntos de API que se exponen a través del objeto Buzón.

Solo debe usar las API comunes para escenarios que no son compatibles con las API específicas de aplicaciones. Para saber cuándo usar las API comunes en lugar de las API específicas de la aplicación, consulte Comprender la API de JavaScript de Office.

¿Por qué la API de Complementos de Office usa programación asincrónica? JavaScript es un lenguaje de un solo subproceso. Si un script invoca un proceso sincrónico de ejecución prolongada del cliente de Office, todos los scripts posteriores se bloquean hasta que se completa ese proceso. Al ser asincrónicos, se asegura de que los complementos de Office respondan y sean rápidos.

Los nombres de todos los métodos asincrónicos de las API comunes terminan con "Async", como los Document.getSelectedDataAsyncmétodos , Binding.getDataAsynco Item.loadCustomPropertiesAsync . Cuando se llama a un método "Async", se ejecuta inmediatamente. El resto del script continúa mientras la operación se completa en el lado cliente. La función de devolución de llamada opcional que se pasa a un método "asincrónico" se ejecuta en cuanto los datos o la operación solicitada están listos. Por lo general, esto ocurre rápidamente, pero puede haber un ligero retraso.

En el diagrama siguiente se muestra el flujo de un método "asincrónico" que lee los datos seleccionados por el usuario en un documento. Cuando se realiza la llamada "Async", el subproceso de JavaScript es libre de realizar cualquier procesamiento adicional del lado cliente (aunque no se muestra ninguno en el diagrama). Cuando se devuelve el método "Async", la devolución de llamada se reanuda en el subproceso. A continuación, el complemento puede acceder a los datos, hacer algo con él y mostrar el resultado. El patrón es el mismo en todas las plataformas.

Diagrama que muestra la interacción de ejecución de comandos a lo largo del tiempo con el usuario, la página del complemento y el servidor de aplicaciones web que hospeda el complemento.

Escritura de la función de devolución de llamada para un método "asincrónico"

La función de devolución de llamada que se pasa como argumento de devolución de llamada a un método "Async" debe declarar un único parámetro. El tiempo de ejecución del complemento usa ese parámetro para proporcionar acceso a un objeto AsyncResult para la función de devolución de llamada.

La función de devolución de llamada puede ser una función anónima o una función con nombre. Una función anónima es útil si solo va a usar su código una vez: puesto que no tiene nombre, no se puede hacer referencia a ella en otra parte del código. Una función con nombre es útil si desea volver a usar la función de devolución de llamada para más de un método "Async".

Escritura de una función de devolución de llamada anónima

La siguiente función de devolución de llamada anónima declara un único parámetro denominado result para los datos devueltos por el cliente. Recupera y escribe esos datos de la propiedad AsyncResult.value cuando se devuelve la devolución de llamada.

function (result) {
    write('Selected data: ' + result.value);
}

En el ejemplo siguiente se muestra esta función de devolución de llamada anónima en el contexto de una llamada completa al Document.getSelectedDataAsync(coercionType, callback) método "Async".

  • El primer argumento coercionType , Office.CoercionType.Text, especifica que se devuelvan los datos seleccionados como una cadena de texto.

  • El segundo argumento de devolución de llamada es la función anónima que se pasa en línea al método . Cuando se ejecuta la función, usa el parámetro result para acceder a la value propiedad del AsyncResult objeto . A continuación, muestra los datos seleccionados por el usuario en el documento.

Office.context.document.getSelectedDataAsync(Office.CoercionType.Text, 
    function (result) {
        write('Selected data: ' + result.value);
    }
});

// Function that writes to a div with id='message' on the page.
function write(message){
    document.getElementById('message').innerText += message; 
}

También puede usar el parámetro de la función de devolución de llamada para acceder a otras propiedades del AsyncResult objeto. Use la propiedad AsyncResult.status para determinar si la llamada se realizó correctamente o produjo errores. Si se produjo un error en la llamada, use la propiedad AsyncResult.error para acceder a un objeto Error para ayudar a decidir qué hacer.

Para obtener más información sobre el getSelectedDataAsync método , consulte Lectura y escritura de datos en la selección activa en un documento o hoja de cálculo.

Escritura de una función de devolución de llamada con nombre

Como alternativa, puede escribir una función con nombre y pasar su nombre al parámetro de devolución de llamada de un método "Async". Aquí, el ejemplo anterior se vuelve a escribir para pasar una función denominada writeDataCallback como parámetro de devolución de llamada .

Office.context.document.getSelectedDataAsync(Office.CoercionType.Text, 
    writeDataCallback);

// Callback to write the selected data to the add-in UI.
function writeDataCallback(result) {
    write('Selected data: ' + result.value);
}

// Function that writes to a div with id='message' on the page.
function write(message){
    document.getElementById('message').innerText += message;
}

Diferencias en lo que se devuelve a la AsyncResult.value propiedad

Las asyncContextpropiedades , statusy error del AsyncResult objeto devuelven los mismos tipos de información a las funciones de devolución de llamada que se pasan a todos los métodos "asincrónicos". Sin embargo, lo que se devuelve a la AsyncResult.value propiedad varía en función de la funcionalidad del método "Async".

Por ejemplo, los addHandlerAsync métodos (de los objetos Binding, CustomXmlPart, Document, RoamingSettings y Settings ) se usan para agregar funciones de controlador de eventos. La AsyncResult.value propiedad de esas funciones de devolución de llamada siempre devuelve undefined, ya que no se tiene acceso a ningún objeto ni a datos al agregar un controlador de eventos.

Por otro lado, si llama al Document.getSelectedDataAsync método , devuelve los datos que el usuario seleccionó en el documento como la AsyncResult.value propiedad en la devolución de llamada. O bien, si llama al método Bindings.getAllAsync , devuelve una matriz de todos los Binding objetos del documento.

Para obtener una descripción de lo que se devuelve a la AsyncResult.value propiedad de un Async método, vea la callback sección del tema de referencia de ese método.

Patrones de programación asincrónica

Las API comunes de la API de JavaScript de Office admiten dos tipos de patrones de programación asincrónica.

  • Devoluciones de llamada anidadas
  • Promesas

Nota:

En la versión actual de la API de JavaScript de Office, la compatibilidad integrada con el patrón de promesas solo funciona con código para enlaces en hojas de cálculo de Excel y documentos Word. Sin embargo, puede encapsular otras funciones que tienen devoluciones de llamada dentro de su propia función de devolución personalizada Promise. Para obtener más información, vea Encapsular las API comunes en las funciones que devuelven promesas.

Programación asincrónica con funciones anidadas de devolución de llamada

A menudo, hay que llevar a cabo dos operaciones asincrónicas o más para finalizar una tarea. Para hacerlo, puede anidar una llamada "Async" dentro de otra.

En el siguiente código de ejemplo se han anidado dos llamadas asincrónicas.

  • Primero se llama al método Bindings.getByIdAsync para tener acceso a un enlace en el documento denominado "MyBinding". El AsyncResult objeto devuelto al result parámetro de esa devolución de llamada proporciona acceso al objeto de enlace especificado desde la AsyncResult.value propiedad .
  • A continuación, se usa el objeto de enlace al que se accede desde el primer result parámetro para llamar al método Binding.getDataAsync .
  • Por último, el result2 parámetro de la devolución de llamada que se pasa al Binding.getDataAsync método se usa para mostrar los datos en el enlace.
function readData() {
    Office.context.document.bindings.getByIdAsync("MyBinding", function (result) {
        result.value.getDataAsync({ coercionType: 'text' }, function (result2) {
            write(result2.value);
        });
    });
}

// Function that writes to a div with id='message' on the page.
function write(message){
    document.getElementById('message').innerText += message; 
}

Este patrón de devolución de llamada anidado básico se puede usar para todos los métodos asincrónicos de las API comunes.

Programación asincrónica con el patrón de promesas para obtener acceso a los datos de los enlaces

En lugar de pasar una función de devolución de llamada y esperar a que la función vuelva antes de que el script continúe, el patrón de programación de promesas devuelve inmediatamente un Promise objeto que representa su resultado previsto. Sin embargo, a diferencia de la programación sincrónica verdadera, el cumplimiento del resultado prometido se aplaza realmente hasta que el entorno en tiempo de ejecución de complementos de Office completa la solicitud. Se proporciona un controlador onError para cubrir situaciones en las que no se puede cumplir con la solicitud.

Las API comunes proporcionan la función Office.select para admitir el patrón de promesas al trabajar con objetos de enlace existentes. El objeto de promesa devuelto a la Office.select función solo admite los cuatro métodos a los que se puede acceder directamente desde el objeto Binding .

El patrón de promesas para trabajar con enlaces adopta este formato.

Office.select( selectorExpression,onError).BindingObjectAsyncMethod;

El parámetro selectorExpression toma el formato "bindings#bindingId", donde bindingId es el nombre ( id) de un enlace que creó en el documento o hoja de cálculo (mediante uno de los métodos "addFrom" de la Bindings colección: addFromNamedItemAsync, addFromPromptAsynco addFromSelectionAsync). El ejemplo selectorExpression de bindings#cities especifica que desea acceder al enlace con un identificador de "cities".

El parámetro onError es una función de control de errores que toma un único parámetro de tipo AsyncResult. Esto se usa para tener acceso a un Error objeto si la select función no puede acceder al enlace especificado. En el siguiente ejemplo, se muestra una función de tratamiento de errores básica que se puede transferir al parámetro onError.

function onError(result){
    const err = result.error;
    write(err.name + ": " + err.message);
}

// Function that writes to a div with id='message' on the page.
function write(message){
    document.getElementById('message').innerText += message; 
}

Reemplace el marcador de posición BindingObjectAsyncMethod por una llamada a cualquiera de los cuatro Binding métodos de objeto admitidos por el objeto de promesa: getDataAsync, setDataAsync, addHandlerAsynco removeHandlerAsync. Las llamadas a estos métodos no son compatibles con promesas adicionales. En ese caso, debe usar el patrón de función de devolución de llamada anidada.

Una vez que se cumple una Binding promesa de objeto, se puede reutilizar en la llamada al método encadenado como si fuera un enlace. Si se ejecuta correctamente, el tiempo de ejecución del complemento no volverá a intentar de forma asincrónica cumplir la promesa. Si no se puede cumplir la Binding promesa de objeto, el tiempo de ejecución del complemento intentará de nuevo acceder al objeto de enlace la próxima vez que se invoque uno de sus métodos asincrónicos.

En el ejemplo siguiente se usa la select función para recuperar un enlace con el id "cities" de la Bindings colección y, a continuación, se llama al método addHandlerAsync para agregar un controlador de eventos para el evento dataChanged del enlace.

function addBindingDataChangedEventHandler() {
    Office.select("bindings#cities", function onError(){/* error handling code */}).addHandlerAsync(Office.EventType.BindingDataChanged,
    function (eventArgs) {
        doSomethingWithBinding(eventArgs.binding);
    });
}

Importante

La Binding promesa de objeto devuelta por la Office.select función proporciona acceso solo a los cuatro métodos del Binding objeto. Si necesita tener acceso a cualquiera de los demás miembros del Binding objeto, en su lugar debe usar la Document.bindings propiedad o Bindings.getByIdAsyncBindings.getAllAsync los métodos para recuperar el Binding objeto.

Pasar parámetros opcionales a métodos asincrónicos

La sintaxis común para todos los métodos "Async" sigue este patrón.

asyncMethod(requiredParameters, [optionalParameters],callbackFunction);

Todos los métodos asincrónicos admiten parámetros opcionales. Se pasan como un objeto JavaScript. El objeto que contiene los parámetros opcionales es una colección desordenada de pares clave-valor. Puede crear el objeto que contiene parámetros opcionales insertados, o bien crear un options objeto y pasarlo como parámetro options .

Pasar parámetros opcionales insertados

Este es un ejemplo del método Document.setSelectedDataAsync con parámetros opcionales definidos en línea. Los dos parámetros opcionales, coercionType y asyncContext, se definen como un objeto JavaScript anónimo.

Office.context.document.setSelectedDataAsync(
    "<html><body>hello world</body></html>",
    {coercionType: "html", asyncContext: 42},
    function(asyncResult) {
        write(asyncResult.status + " " + asyncResult.asyncContext);
    }
)

// Function that writes to a div with id='message' on the page.
function write(message){
    document.getElementById('message').innerText += message; 
}

Pasar parámetros opcionales en un objeto con nombre

Como alternativa, puede crear un objeto con nombre que especifique los parámetros opcionales por separado de la llamada al método y, a continuación, pasar el objeto como argumento options . En el ejemplo siguiente se muestra una forma de crear un options objeto, donde parameter1, value1, etc., son marcadores de posición para los nombres y valores de parámetros reales.

const options = {
    parameter1: value1,
    parameter2: value2,
    ...
    parameterN: valueN
};

Es similar al ejemplo siguiente cuando se usa para especificar los parámetros ValueFormat y FilterType.

const options = {
    valueFormat: "unformatted",
    filterType: "all"
};

Esta es otra forma de crear el options objeto.

const options = {};
options[parameter1] = value1;
options[parameter2] = value2;
...
options[parameterN] = valueN;

Que es similar al ejemplo siguiente cuando se usa para especificar los ValueFormat parámetros y FilterType :

const options = {};
options["ValueFormat"] = "unformatted";
options["FilterType"] = "all";

En el Document.setSelectedDataAsync ejemplo siguiente se muestra cómo llamar al método especificando parámetros opcionales en un options objeto .

const options = {
   coercionType: "html",
   asyncContext: 42
};

document.setSelectedDataAsync(
    "<html><body>hello world</body></html>",
    options,
    function(asyncResult) {
        write(asyncResult.status + " " + asyncResult.asyncContext);
    }
)

// Function that writes to a div with id='message' on the page.
function write(message){
    document.getElementById('message').innerText += message; 
}

En ambos ejemplos de parámetros opcionales, el parámetro de devolución de llamada se especifica como el último parámetro (siguiendo los parámetros opcionales insertados o siguiendo el objeto de argumento options ). Como alternativa, puede especificar el parámetro de devolución de llamada dentro del objeto JavaScript insertado o en el options objeto . Sin embargo, puede pasar el parámetro de devolución de llamada en una sola ubicación: en el options objeto (insertado o creado externamente) o como último parámetro, pero no en ambos.

Encapsular las API comunes en Promisefunciones que devuelven

Los métodos De API común (y API de Outlook) no devuelven Promesas. Por lo tanto, no puede usar await para pausar la ejecución hasta que se complete la operación asincrónica. Si necesita await comportamiento, ajuste la llamada al método en un objeto creado Promiseexplícitamente.

El patrón básico consiste en crear un método asincrónico que devuelve un objeto Promise inmediatamente y resuelve ese objeto Promise cuando se completa el método interno o rechaza el objeto si se produce un error en el método. A continuación puede ver un ejemplo simple.

function getDocumentFilePath() {
    return new OfficeExtension.Promise(function (resolve, reject) {
        try {
            Office.context.document.getFilePropertiesAsync(function (asyncResult) {
                resolve(asyncResult.value.url);
            });
        }
        catch (error) {
            reject(WordMarkdownConversion.errorHandler(error));
        }
    })
}

Cuando es necesario esperar a esta función, se puede llamar a ella con la await palabra clave o pasarla a una then función.

Nota:

Esta técnica es especialmente útil cuando se necesita llamar a una API común dentro de una llamada de la función en un modelo de objetos específico de la run aplicación. Para obtener un ejemplo de la getDocumentFilePath función que se usa de esta manera, consulte el archivo Home.js en el ejemplo Word-Add-in-JavaScript-MDConversion.

Recursos adicionales