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.getSelectedDataAsync
Binding.getDataAsync
ou 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.
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 doAsyncResult
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 asyncContext
propriedades , status
e 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.
Observação
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 aoresult
parâmetro dessa chamada de retorno fornece acesso ao objeto de enlace especificado aAsyncResult.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 aoBinding.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
, addFromPromptAsync
ou 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
, addHandlerAsync
ou 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 document
propriedades , id
ou 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;
}
Observação
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 parameter1
que , value1
e 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";
Observação
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.
Observação
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);
}
});
}
});
});
}