Programação assíncrona em Suplementos do Office

Importante

Este artigo se aplica às APIs comuns, o modelo de API JavaScript do Office que foi introduzido com o Office 2013. Essas APIs incluem recursos como interface de usuário, caixas de diálogo e configurações de cliente, que são comuns entre vários tipos de aplicativos do Office. Os suplementos do Outlook usam exclusivamente APIs comuns, especialmente o subconjunto de APIs expostos por meio do objetoCaixa de Correio.

Você só deve usar APIs comuns para cenários que não têm suporte por APIs específicas do aplicativo. Para saber quando usar APIs comuns em vez de APIs específicas do aplicativo, confira Entendendo a API de JavaScript do Office.

Por que a API de Suplementos do Office usa a programação assíncrona? JavaScript é uma linguagem de thread único. Se um script invocar um processo síncrono de longa execução do cliente do Office, todos os scripts subsequentes serão bloqueados até que esse processo seja concluído. Usando a programação assíncrona, os Suplementos do Office permanecem responsivos e rápidos.

Os nomes de todos os métodos assíncronos nas APIs comuns terminam com "Async", como , Document.getSelectedDataAsyncBinding.getDataAsync, ou Item.loadCustomPropertiesAsync métodos. Quando um método "Async" é chamado, ele é executado imediatamente. O restante do script continua enquanto a operação é concluída no lado do cliente. A função de retorno de chamada opcional que você passa para um método "Assíncrono" é executada assim que os dados ou a operação solicitada estiver pronta. Isso geralmente ocorre prontamente, mas pode haver um pequeno atraso.

O diagrama a seguir mostra o fluxo de um método "Assíncrono" que lê os dados que o usuário selecionou em um documento. Quando a chamada "Async" é feita, o thread JavaScript fica livre para executar qualquer processamento adicional do lado do cliente (embora nenhum seja mostrado no diagrama). Quando o método "Async" retorna, o retorno de chamada é retomado no thread. O suplemento pode então acessar dados, fazer algo com eles e exibir o resultado. O padrão é o mesmo em todas as plataformas.

Diagrama mostrando a interação de execução de comando ao longo do tempo com o usuário, a página do suplemento e o servidor de aplicativos Web que hospeda o suplemento.

Gravar a função de retorno de chamada para um método "Assíncrono"

A função de retorno de chamada que você passa como argumento de retorno de chamada para um método "Assíncrono" deve declarar um único parâmetro. O runtime do suplemento usa esse parâmetro para fornecer acesso a um objeto AsyncResult para a função de retorno de chamada.

A função de retorno de chamada pode ser uma função anônima ou uma função nomeada. Uma função anônima é útil se você for usar seu código apenas uma vez - como ela não tem nome, você não pode referenciá-la em outra parte do código. Uma função nomeada é útil se você quiser reutilizar a função retorno de chamada para mais de um método "Async".

Escreva uma função de retorno de chamada anônima

A função de retorno de chamada anônima a seguir declara um único parâmetro nomeado result para os dados retornados pelo cliente. Ele recupera e grava esses dados da propriedade AsyncResult.value quando o retorno de chamada retorna.

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

O exemplo a seguir mostra essa função de retorno de chamada anônima no contexto de uma chamada completa do método "Async" para o Document.getSelectedDataAsync(coercionType, callback) método.

  • O primeiro argumento coercionType , Office.CoercionType.Text, especifica retornar os dados selecionados como uma cadeia de texto.

  • O segundo argumento de retorno de chamada é a função anônima passada embutida para o método. Quando a função é executada, ela usa o parâmetro result para acessar a value propriedade do AsyncResult objeto. Em seguida, exibe os dados selecionados pelo usuário no 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; 
}

Você também pode usar o parâmetro de sua função de retorno de chamada para acessar outras propriedades do AsyncResult objeto. Use a propriedade AsyncResult.status para determinar se a chamada teve êxito ou falhou. Se sua chamada falhou, use a propriedade AsyncResult.error para acessar um objeto Error para ajudar a decidir o que fazer.

Para obter mais informações sobre o getSelectedDataAsync método, consulte Ler e gravar dados na seleção ativa em um documento ou planilha.

Escreva uma função de retorno de chamada nomeada

Como alternativa, você pode escrever uma função nomeada e passar seu nome para o parâmetro de retorno de chamada de um método "Async". Aqui, o exemplo anterior é reescrito para transmitir uma função chamada writeDataCallback de parâmetro de retorno .

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

Diferenças no que é devolvido à AsyncResult.value propriedade

As asyncContextpropriedades , statuse error do AsyncResult objeto retornam os mesmos tipos de informações para as funções de retorno de chamada passadas para todos os métodos "Assíncronos". No entanto, o que é retornado para a AsyncResult.value propriedade varia dependendo da funcionalidade do método "Assíncrono".

Por exemplo, addHandlerAsync os métodos dos objetos Binding, CustomXmlPart, Document, RoamingSettings e Settings adicionam funções de manipulador de eventos. A AsyncResult.value propriedade nessas funções de retorno de chamada sempre retorna indefinido, pois o método não acessa nenhum dado ou objeto quando adiciona um manipulador de eventos.

Por outro lado, se você chamar o Document.getSelectedDataAsync método, ele retornará os dados que o usuário selecionou no documento como a AsyncResult.value propriedade no retorno de chamada. Ou, se você chamar o método Bindings.getAllAsync , ele retornará uma matriz de todos os Binding objetos no documento.

Para obter uma descrição do que é retornado para a AsyncResult.value propriedade de um Async método, consulte a callback seção do tópico de referência desse método.

Padrões de programação assíncrona

As APIs comuns na API JavaScript do Office oferecem suporte a dois tipos de padrões de programação assíncrona.

  • Retornos de chamada aninhados
  • Promessas

Observação

Na versão atual da API JavaScript do Office, o suporte interno para o padrão promises só funciona com código para associações em planilhas do Excel e documentos do Word. No entanto, você pode encapsular outras funções que têm retornos de chamada dentro de sua própria função de retorno personalizado Promise. Para obter mais informações, consulte Encapsular APIs comuns em funções de retorno de promessa.

Programação assíncrona usando funções aninhadas de retorno de chamada

Frequentemente, você precisa executar duas ou mais operações assíncronas para concluir uma tarefa. Para realizar essa tarefa, você pode aninhar uma chamada "Assíncrona" dentro de outra.

O exemplo de código a seguir aninha duas ou mais chamadas assíncronas.

  • Primeiro, o código chama Bindings.getByIdAsync para acessar uma associação no documento chamada "MyBinding". O AsyncResult objeto retornado ao result parâmetro desse retorno de chamada fornece acesso ao objeto de associação especificado da AsyncResult.value propriedade.
  • Em seguida, o código usa o objeto de associação acessado do primeiro result parâmetro para chamar Binding.getDataAsync.
  • Por fim, o código usa o result2 parâmetro do retorno de chamada passado para o Binding.getDataAsync método para exibir os dados na associação.
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; 
}

Você pode usar esse padrão básico de retorno de chamada aninhado para todos os métodos assíncronos nas APIs comuns.

Programação assíncrona usando o padrão de promessas para acessar dados em associações

Em vez de passar uma função de retorno de chamada e esperar que a função retorne antes que o script continue, o padrão de programação promises retorna imediatamente um Promise objeto que representa o resultado pretendido. No entanto, ao contrário da programação síncrona verdadeira, nos bastidores, o ambiente de tempo de execução dos Suplementos do Office adia o cumprimento do resultado prometido até concluir a solicitação. Um manipulador onError abrange situações em que a solicitação não pode ser atendida.

As APIs comuns fornecem a função Office.select para dar suporte ao padrão de promessas ao trabalhar com objetos de associação existentes. O objeto promise que Office.select retorna dá suporte apenas aos quatro métodos diretamente acessíveis do objeto Binding .

O padrão de promessas para trabalhar com associações assume esta forma.

Office.select( selectorExpression,onError).BindingObjectAsyncMethod;

O parâmetro selectorExpression assume o formato "bindings#bindingId", onde bindingId é o nome (id) de uma associação que você criou no documento ou planilha (usando um dos métodos "addFrom" da Bindings coleção: addFromNamedItemAsync, addFromPromptAsync, ou addFromSelectionAsync). O exemplo selectorExpression of bindings#cities especifica que você deseja acessar a associação com uma id de cities.

O parâmetro onError é uma função de tratamento de erros que usa um único parâmetro do tipo AsyncResult. Isso é usado para acessar um Error objeto se a select função não conseguir acessar a associação especificada. O exemplo a seguir mostra uma função de manipulador de erro básica que pode ser transmitida para o 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; 
}

Substitua o espaço reservado BindingObjectAsyncMethod por uma chamada para qualquer um dos quatro Binding métodos de objeto compatíveis com o objeto de promessa: getDataAsync, setDataAsync, addHandlerAsync, ou removeHandlerAsync. As chamadas para esses métodos não oferecem suporte a promessas adicionais. Nesse caso, você deve usar o padrão de função de retorno de chamada aninhado.

Depois que uma promessa de Binding objeto é cumprida, ela pode ser reutilizada na chamada de método encadeada como se fosse uma associação. Se for bem-sucedido, o runtime do suplemento não tentará cumprir a promessa de forma assíncrona. Se a Binding promessa do objeto não puder ser cumprida, o runtime do suplemento tentará acessar novamente o objeto de associação na próxima vez que um de seus métodos assíncronos for invocado.

O exemplo a seguir usa a select função para recuperar uma associação com a idcities coleção e, em seguida, chama o método addHandlerAsync para adicionar um manipulador de Bindings eventos para o evento dataChanged da associação.

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

Importante

A Binding promessa de objeto que a Office.select função retorna fornece acesso apenas aos quatro métodos do Binding objeto. Se você precisar acessar qualquer um dos outros membros do Binding objeto, deverá usar a propriedade eBindings.getByIdAsync/Document.bindingsou Bindings.getAllAsync métodos para recuperar o Binding objeto.

Passar parâmetros opcionais para métodos assíncronos

A sintaxe comum para todos os métodos "Assíncronos" segue esse padrão.

asyncMethod(Parâmetros obrigatórios, [parâmetros], opcionaiscallbackFunction);

Todos os métodos assíncronos dão suporte a parâmetros opcionais. Eles são passados como um objeto JavaScript. O objeto que contém os parâmetros opcionais é uma coleção não ordenada de pares chave-valor. Você pode criar o objeto que contém parâmetros opcionais embutidos ou criando um options objeto e passando-o como o parâmetro de opções .

Passar parâmetros opcionais embutidos

O exemplo a seguir mostra o método Document.setSelectedDataAsync com parâmetros opcionais definidos embutidos. Os dois parâmetros opcionais, coercionType e asyncContext, são definidos como um 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; 
}

Passar parâmetros opcionais em um objeto nomeado

Como alternativa, você pode criar um objeto nomeado que especifica os parâmetros opcionais separadamente da chamada do método e, em seguida, passar o objeto como o argumento options . O exemplo a seguir mostra uma maneira de criar um options objeto, em que parameter1, value1, e assim por diante são espaços reservados para os nomes e valores de parâmetros reais.

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

Que é semelhante ao exemplo a seguir quando usado para especificar os parâmetros ValueFormat e FilterType.

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

Aqui está outra maneira de criar o options objeto.

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

Que se parece com o exemplo a seguir quando usado para especificar os ValueFormat parâmetros and FilterType :

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

O exemplo a seguir mostra como chamar o Document.setSelectedDataAsync método especificando parâmetros opcionais em um 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; 
}

Em ambos os exemplos de parâmetros opcionais, você especifica o parâmetro de retorno de chamada como o último parâmetro (seguindo os parâmetros opcionais embutidos ou seguindo o objeto de argumento options ). Como alternativa, você pode especificar o parâmetro de retorno de chamada dentro do objeto JavaScript embutido ou no options objeto. No entanto, você pode passar o parâmetro de retorno de chamada em apenas um local: no options objeto (embutido ou criado externamente) ou como o último parâmetro, mas não em ambos.

Encapsular APIs comuns em Promisefunções de retorno

Os métodos comuns de API e API do Outlook não retornam Promessas. Portanto, você não pode usar await para pausar a execução até que a operação assíncrona seja concluída. Se você precisar de await comportamento, envolva a chamada de método em um arquivo .Promise

O padrão básico é criar um método assíncrono que retorna imediatamente um objeto Promise e resolve esse objeto Promise quando o método interno termina ou rejeita o objeto se o método falhar. O exemplo a seguir mostra esse padrão.

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

Quando essa função precisa ser aguardada, ela pode ser chamada com a await palavra-chave ou passada para uma then função.

Observação

Essa técnica é especialmente útil quando você precisa chamar uma API comum dentro de uma chamada da função em um modelo de objeto específico do run aplicativo. Para obter um exemplo da getDocumentFilePath função que está sendo usada dessa maneira, consulte o Home.js de arquivo no exemplo Word-Add-in-JavaScript-MDConversion.

Confira também