Programação assíncrona em Suplementos do Office

Importante

Este artigo aplica-se às APIs Comuns, o modelo da 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? Como o JavaScript é uma linguagem de thread único, se o script invocar um processo síncrono demorado, todas as execuções subsequentes do script serão bloqueadas até que o processo seja concluído. Uma vez que determinadas operações em clientes Web do Office (mas também clientes de ambiente de trabalho) podem bloquear a execução se forem executadas de forma síncrona, a maioria das APIs JavaScript do Office foram concebidas para serem executadas de forma assíncrona. Isto garante que os Suplementos do Office são reativos e rápidos. Em geral, isso também requer que você escreva funções de retorno de chamada ao trabalhar com esses métodos assíncronos.

Os nomes de todos os métodos assíncronos na API terminam com "Assíncrono", como , Document.getSelectedDataAsyncBinding.getDataAsyncou Item.loadCustomPropertiesAsync métodos. Quando um método "Async" é chamado, ele é executado imediatamente e qualquer execução subsequente do script poderá continuar. A função de retorno de chamada opcional que você passar para um método de "Async" é executada assim que os dados ou a operação solicitada está pronta. Isso geralmente ocorre imediatamente, mas pode haver um pequeno atraso antes de retornar.

O diagrama seguinte mostra o fluxo de execução de uma chamada para um método "Assíncrono" que lê os dados que o utilizador selecionou num documento aberto no Word ou Excel baseado no servidor. No momento em que a chamada "Assíncrona" é efetuada, o thread de execução javaScript é gratuito para efetuar qualquer processamento adicional do lado do cliente (embora nenhum seja apresentado no diagrama). Quando o método "Assíncrono" é devolvido, a chamada de retorno retoma a execução no thread e o suplemento pode aceder aos dados, fazer algo com o mesmo e apresentar o resultado. O mesmo padrão de execução assíncrona mantém-se ao trabalhar com aplicações cliente do Office no Windows ou no Mac.

Diagrama a mostrar a interação de execução de comandos ao longo do tempo com o utilizador, a página do suplemento e o servidor da aplicação Web que aloja o suplemento.

O suporte a esse design assíncrono em clientes Web e avançados faz parte das metas de design "gravar plataforma cruzada já executada" do modelo de desenvolvimento de Suplementos do Office. Por exemplo, pode criar um suplemento de conteúdo ou painel de tarefas com uma base de código única que será executada no Excel no Windows e no Excel na Web.

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

A função de chamada de retorno que transmitir como argumento de chamada de retorno para um método "Assíncrono" tem de declarar um único parâmetro que o runtime do suplemento irá utilizar para fornecer acesso a um objeto AsyncResult quando a função de chamada de retorno é executada. Você pode gravar:

  • Uma função anónima que tem de ser escrita e transmitida diretamente em linha com a chamada para o método "Assíncrono" como o parâmetro de chamada de retorno do método "Assíncrono".

  • Uma função nomeada, ao transmitir o nome dessa função como o parâmetro de chamada de retorno de um método "Assíncrono".

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

Escrever uma função de chamada de retorno anónima

A seguinte função de chamada de retorno anónima declara um único parâmetro com o nome result que obtém dados da propriedade AsyncResult.value quando a chamada de retorno é devolvida.

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

O exemplo seguinte mostra como transmitir esta função de chamada de retorno anónima em linha no contexto de uma chamada de método "Assíncrona" completa para o Document.getSelectedDataAsync método .

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

  • O segundo argumento de chamada de retorno é a função anónima transmitida em linha para o método . Quando a função é executada, utiliza o parâmetro de resultado para aceder value à propriedade do AsyncResult objeto para apresentar os dados selecionados pelo utilizador 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; 
}

Também pode utilizar o parâmetro da função de chamada de retorno para aceder a outras propriedades do AsyncResult objeto. Use a propriedade AsyncResult.status para determinar se a chamada teve êxito ou falhou. Se sua chamada falhar, você pode usar a propriedade AsyncResult.error para acessar um objeto Error para informações sobre o erro.

Para obter mais informações sobre como utilizar o getSelectedDataAsync método , consulte Ler e escrever dados na seleção ativa num documento ou folha de cálculo.

Escrever uma função de chamada de retorno com nome

Em alternativa, pode escrever uma função com nome e transmitir o respetivo nome para o parâmetro de chamada de retorno de um método "Assíncrono". Por exemplo, o exemplo anterior pode ser reescrito para transmitir uma função chamada writeDataCallback como o parâmetro callback assim.

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 entre o que é retornado para a propriedade AsyncResult.value

As asyncContextpropriedades , statuse error do AsyncResult objeto devolvem os mesmos tipos de informações para a função de chamada de retorno transmitida a todos os métodos "Assíncrono". No entanto, o que é devolvido à AsyncResult.value propriedade varia consoante a funcionalidade do método "Assíncrono".

Por exemplo, os addHandlerAsync métodos (dos objetos Binding, CustomXmlPart, Document, RoamingSettings e Settings) são utilizados para adicionar funções de processador de eventos aos itens representados por estes objetos. Pode aceder à propriedade a AsyncResult.value partir da função de chamada de retorno que transmite a qualquer um dos addHandlerAsync métodos, mas uma vez que não estão a ser acedidos dados ou objetos quando adiciona um processador de eventos, a value propriedade devolve sempre indefinido se tentar aceder ao mesmo.

Por outro lado, se chamar o Document.getSelectedDataAsync método , este devolve os dados que o utilizador selecionou no documento para a AsyncResult.value propriedade na chamada de retorno. Em alternativa, se chamar o método Bindings.getAllAsync , devolve uma matriz de todos os Binding objetos no documento. E, se chamar o método Bindings.getByIdAsync , devolve um único Binding objeto.

Para obter uma descrição do que é devolvido à AsyncResult.value propriedade de um Async método, veja a secção "Valor da chamada de retorno" do tópico de referência desse método. Para obter um resumo de todos os objetos que fornecem Async métodos, veja a tabela na parte inferior do tópico de objeto AsyncResult .

Padrões de programação assíncrona

A API JavaScript do Office suporta dois tipos de padrões de programação assíncronos.

  • Usando retornos de chamada aninhados
  • Usando o padrão de promessas

A programação assíncrona com funções de retorno de chamada frequentemente exigem que você aninhe o resultado retornado de um retorno de chamada dentro de dois ou mais retornos de chamada. Se você precisar fazer isso, é possível usar retornos de chamada aninhados de todos os métodos "Async" da API.

Usar retornos de chamada aninhados é um padrão de programação familiar para a maioria dos desenvolvedores de JavaScript, mas códigos com retornos de chamada profundamente aninhados podem ser difíceis de ler e entender. Como alternativa às chamadas de retorno aninhadas, a API JavaScript do Office também suporta uma implementação do padrão de promessas.

Nota

Na versão atual da API JavaScript do Office, o suporte incorporado para o padrão de promessas só funciona com código para enlaces em folhas de cálculo do Excel e documentos do Word. No entanto, pode encapsular outras funções que têm chamadas de retorno dentro da sua própria função de devolução de promessas personalizada. Para obter mais informações, veja Moldar APIs Comuns em Funções que devolvem promessas.

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 fazer isso, você pode aninhar uma chamada "Async" dentro de outra.

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

  • Primeiro, o método Bindings.getByIdAsync é chamado para acessar uma associação no documento chamado "MyBinding". O AsyncResult objeto devolvido ao result parâmetro dessa chamada de retorno fornece acesso ao objeto de enlace especificado a AsyncResult.value partir da propriedade .
  • Em seguida, o objeto de enlace acedido a partir do primeiro result parâmetro é utilizado para chamar o método Binding.getDataAsync .
  • Por fim, o result2 parâmetro da chamada de retorno transmitido ao Binding.getDataAsync método é utilizado para apresentar os dados no 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 padrão básico de chamada de retorno aninhada pode ser utilizado para todos os métodos assíncronos na API de JavaScript do Office.

As seções a seguir mostram como usar funções anônimas ou nomeadas para retornos de chamada aninhados em métodos assíncronos.

Utilizar funções anónimas para chamadas de retorno aninhadas

No exemplo seguinte, duas funções anónimas são declaradas inline e transmitidas para os getByIdAsync métodos e getDataAsync como chamadas de retorno aninhadas. Como as funções são simples e embutidas, a intenção da implementação fica imediatamente clara.

Office.context.document.bindings.getByIdAsync('myBinding', function (bindingResult) {
    bindingResult.value.getDataAsync(function (getResult) {
        if (getResult.status == Office.AsyncResultStatus.Failed) {
            write('Action failed. Error: ' + asyncResult.error.message);
        } else {
            write('Data has been read successfully.');
        }
    });
});

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

Utilizar funções nomeadas para chamadas de retorno aninhadas

Em implementações complexas, pode ser útil usar funções nomeadas para facilitar a leitura, manutenção e reutilização do seu código. No exemplo seguinte, as duas funções anónimas do exemplo na secção anterior foram reescritas como funções com o nome deleteAllData e showResult. Estas funções nomeadas são então transmitidas para os getByIdAsync métodos e deleteAllDataValuesAsync como chamadas de retorno por nome.

Office.context.document.bindings.getByIdAsync('myBinding', deleteAllData);

function deleteAllData(asyncResult) {
    asyncResult.value.deleteAllDataValuesAsync(showResult);
}

function showResult(asyncResult) {
    if (asyncResult.status == Office.AsyncResultStatus.Failed) {
        write('Action failed. Error: ' + asyncResult.error.message);
    } else {
        write('Data has been deleted successfully.');
    }
}

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

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

Em vez de transmitir a função de retorno de chamada e aguardar até que a função retorne antes da continuação da execução, o padrão de programação de promessas retorna imediatamente retorna um objeto de promessa que representa o resultado desejado. No entanto, ao contrário da verdadeira programação síncrona, nos bastidores o cumprimento do resultado prometido é, na verdade, adiado até que o ambiente de tempo de execução dos Suplementos do Office possa concluir a solicitação. Um manipulador onError é fornecido para atender a situações em que a solicitação não pode ser cumprida.

A API JavaScript do Office fornece a função Office.select para suportar o padrão de promessas para trabalhar com objetos de enlace existentes. O objeto promise devolvido à Office.select função suporta apenas os quatro métodos a que pode aceder diretamente a partir do objeto Enlace : getDataAsync, setDataAsync, addHandlerAsync e removeHandlerAsync.

O padrão de promessas para trabalhar com enlaces assume este formulário.

Office.select(selectorExpression, onError).BindingObjectAsyncMethod

O parâmetro selectorExpression assume o formulário "bindings#bindingId", em que bindingId é o nome ( id) de um enlace que criou anteriormente no documento ou folha de cálculo (utilizando um dos métodos "addFrom" da Bindings coleção: addFromNamedItemAsync, addFromPromptAsyncou addFromSelectionAsync). Por exemplo, a expressão bindings#cities do seletor especifica que pretende aceder ao enlace com um ID de "cidades".

O parâmetro onError é uma função de processamento de erros que utiliza um único parâmetro do tipo AsyncResult que pode ser utilizado para aceder a um Error objeto, se a select função não conseguir aceder ao enlace especificado. 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 marcador de posição BindingObjectAsyncMethod por uma chamada para qualquer um dos quatro Binding métodos de objeto suportados pelo objeto promise: getDataAsync, setDataAsync, addHandlerAsyncou removeHandlerAsync. As chamadas para esses métodos não oferecem suporte a promessas adicionais. Você deve chamá-los usando o padrão de função de retorno de chamada aninhado.

Depois de uma Binding promessa de objeto ser cumprida, pode ser reutilizada na chamada de método em cadeia como se fosse um enlace (o runtime do suplemento não voltará a cumprir a promessa de forma assíncrona). Se não for possível cumprir a Binding promessa do objeto, o runtime do suplemento tentará novamente aceder ao objeto de enlace da próxima vez que um dos seus métodos assíncronos for invocado.

O exemplo de código seguinte utiliza a select função para obter um enlace com o id "cities" da Bindings coleção e, em seguida, chama o método addHandlerAsync para adicionar um processador de eventos para o evento dataChanged do enlace.

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 devolvida pela Office.select função fornece acesso apenas aos quatro métodos do Binding objeto. Se precisar de aceder a qualquer um dos outros membros do Binding objeto, tem de utilizar a Document.bindings propriedade e Bindings.getByIdAsync os métodos ou Bindings.getAllAsync para obter o Binding objeto. Por exemplo, se precisar de aceder a qualquer uma das Binding propriedades do objeto (as documentpropriedades , idou type ) ou precisar de aceder às propriedades dos objetos MatrixBinding ou TableBinding , tem de utilizar os getByIdAsync métodos ou getAllAsync para obter um Binding objeto.

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

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

AsyncMethod(Parâmetros Obrigatórios, [Parâmetros Opcionais],CallbackFunction);

Todos os métodos assíncronos suportam parâmetros opcionais, que são transmitidos como um objeto JavaScript que contém um ou mais parâmetros opcionais. O objeto que contém os parâmetros opcionais é uma coleção não ordenada de pares chave-valor com o caráter ":" que separa a chave e o valor. Cada par do objeto é separado por vírgula e o conjunto completo de pares é incluído entre chaves. A chave é o nome do parâmetro e o valor é o valor a ser transmitido para esse parâmetro.

Pode criar o objeto que contém parâmetros opcionais inline ou ao criar um options objeto e transmiti-lo como o parâmetro de opções .

Transmitir parâmetros opcionais inline

Por exemplo, a sintaxe para chamar o método Document.setSelectedDataAsync com parâmetros opcionais embutidos tem esta aparência:

 Office.context.document.setSelectedDataAsync(data, {coercionType: 'coercionType', asyncContext: 'asyncContext'},callback);

Nesta forma da sintaxe de chamada, os dois parâmetros opcionais, coercionType e asyncContext, são definidos como um objeto JavaScript anónimo inline entre chavetas.

O exemplo seguinte mostra como chamar para o Document.setSelectedDataAsync método ao especificar parâmetros opcionais inline.

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

Nota

Pode especificar parâmetros opcionais em qualquer ordem no objeto de parâmetro, desde que os respetivos nomes sejam especificados corretamente.

Transmitir parâmetros opcionais num objeto de opções

Em alternativa, pode criar um objeto com o nome options que especifica os parâmetros opcionais separadamente da chamada de método e, em seguida, transmitir o options objeto como o argumento de opções .

O exemplo seguinte mostra uma forma de criar o options objeto, em parameter1que , value1e assim por diante, são marcadores de posição para os nomes e valores dos 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"
};

Eis outra forma de criar o options objeto.

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

O que se assemelha ao exemplo seguinte quando utilizado para especificar os ValueFormat parâmetros e FilterType :

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

Nota

Ao utilizar qualquer um dos métodos de criação do options objeto, pode especificar parâmetros opcionais por qualquer ordem, desde que os respetivos nomes sejam especificados corretamente.

O exemplo seguinte mostra como chamar o Document.setSelectedDataAsync método ao especificar parâmetros opcionais num 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, o parâmetro de chamada de retorno é especificado como o último parâmetro (seguindo os parâmetros opcionais inline ou seguindo o objeto de argumento opções ). Em alternativa, pode especificar o parâmetro de chamada de retorno dentro do objeto JavaScript inline ou no options objeto . No entanto, pode transmitir o parâmetro de chamada de retorno em apenas uma localização: no options objeto (inline ou criado externamente) ou como o último parâmetro, mas não ambos.

Encapsular APIs Comuns em funções que devolvem promessas

Os métodos da API Comum (e da API do Outlook) não devolvem Promessas. Por conseguinte, não pode utilizar a opção aguardar para colocar a execução em pausa até que a operação assíncrona seja concluída. Se precisar de await comportamento, pode encapsular a chamada de método numa Promessa criada explicitamente.

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

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 esta função precisa de ser aguardada, pode ser chamada com a await palavra-chave ou transmitida a uma then função.

Nota

Esta técnica é especialmente útil quando precisa de chamar uma API Comum dentro de uma chamada da run função num modelo de objeto específico da aplicação. Para obter um exemplo da função que está a getDocumentFilePath ser utilizada desta forma, veja o ficheiro Home.js no exemplo word-add-in-JavaScript-MDConversion.

Segue-se um exemplo que utiliza TypeScript.

readDocumentFileAsync(): Promise<any> {
    return new Promise((resolve, reject) => {
        const chunkSize = 65536;
        const self = this;

        Office.context.document.getFileAsync(Office.FileType.Compressed, { sliceSize: chunkSize }, (asyncResult) => {
            if (asyncResult.status === Office.AsyncResultStatus.Failed) {
                reject(asyncResult.error);
            } else {
                // `getAllSlices` is a Promise-wrapped implementation of File.getSliceAsync.
                self.getAllSlices(asyncResult.value).then(result => {
                    if (result.IsSuccess) {
                        resolve(result.Data);
                    } else {
                        reject(asyncResult.error);
                    }
                });
            }
        });
    });
}

Confira também