Поделиться через


Асинхронное программирование в случае надстроек Office

Важно!

Эта статья относится к общим API, модели API JavaScript для Office, которая была представлена в Office 2013. Эти API-интерфейсы включают такие компоненты, как пользовательский интерфейс, диалоговые окна и параметры клиентов, общие для нескольких типов приложений Office. Надстройки Outlook используют только общие API, в частности подмножество API, предоставленных в объекте Mailbox.

Вы должны использовать общие API только в сценариях, которые не поддерживаются API-интерфейсами для определенных приложений. Сведения о том, как использовать общие API вместо API для определенных приложений, см. в статье Общие сведения об API JavaScript для Office.

Почему в API Надстройки Office используется асинхронное программирование? JavaScript — это однопоточный язык. Если скрипт вызывает длительный синхронный процесс клиента Office, все последующие скрипты блокируются до завершения этого процесса. Асинхронный режим обеспечивает быстрое и быстрое реагирование надстроек Office.

Имена всех асинхронных методов в общих API заканчиваются на "Async", например Document.getSelectedDataAsyncметоды , Binding.getDataAsyncили Item.loadCustomPropertiesAsync . При вызове асинхронного метода он выполняется немедленно. Остальная часть скрипта продолжается, пока операция завершается на стороне клиента. Необязательная функция обратного вызова, передаваемая в асинхронный метод, запускается, как только данные или запрошенная операция будут готовы. Обычно это происходит быстро, но может быть небольшая задержка.

На следующей схеме показан поток асинхронного метода, который считывает данные, выбранные пользователем в документе. При выполнении асинхронного вызова поток JavaScript может выполнять дополнительную обработку на стороне клиента (хотя ни один из них не показан на схеме). Когда возвращается метод Async, обратный вызов возобновляется в потоке. Затем надстройка может получить доступ к данным, выполнить с ним что-то и отобразить результат. Шаблон одинаков на разных платформах.

Схема, показывающая взаимодействие выполнения команд с течением времени с пользователем, страницей надстройки и сервером веб-приложений, на котором размещается надстройка.

Запись функции обратного вызова для асинхронного метода

Функция обратного вызова, передаваемая в качестве аргумента обратного вызова методу Async, должна объявить один параметр. Среда выполнения надстройки использует этот параметр для предоставления доступа к объекту AsyncResult для функции обратного вызова.

Функция обратного вызова может быть анонимной или именованной функцией. Анонимную функцию удобно использовать, если код такой функции будет использован всего один раз (так как у нее нет имени, вы не сможете сослаться на нее в другой части кода). Именованные функции применяются, если необходимо многократно использовать функцию обратного вызова для нескольких асинхронных методов.

Написание функции анонимного обратного вызова

Следующая анонимная функция обратного вызова объявляет один параметр с именем result для данных, возвращаемых клиентом. Он извлекает и записывает эти данные из свойства AsyncResult.value при возврате обратного вызова.

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

В следующем примере показана эта анонимная функция обратного вызова в контексте полного асинхронного вызова метода метода Document.getSelectedDataAsync(coercionType, callback) .

  • Первый аргумент coercionType , указывает для Office.CoercionType.Textвозврата выбранных данных в виде строки текста.

  • Второй аргумент обратного вызова — это анонимная функция, передаваемая в метод . При запуске функции она использует параметр result для доступа к свойству valueAsyncResult объекта . Затем отображаются данные, выбранные пользователем в документе.

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

Вы также можете использовать параметр функции обратного вызова для доступа к другим свойствам AsyncResult объекта . Используйте свойство AsyncResult.status, чтобы определить, успешно ли был выполнен вызов. Если вызов завершился сбоем, используйте свойство AsyncResult.error , чтобы получить доступ к объекту Error , чтобы решить, что делать.

Дополнительные сведения о методе см. в getSelectedDataAsync статье Чтение и запись данных в активный выделенный фрагмент в документе или электронной таблице.

Написание именованной функции обратного вызова

Кроме того, можно написать именованную функцию и передать ее имя в параметр обратного вызова метода Async. Здесь предыдущий пример перезаписывается для передачи функции с именем writeDataCallback в качестве параметра обратного вызова .

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

Различия в том, что возвращается в AsyncResult.value свойство

Свойства asyncContext, statusи error объекта возвращают одинаковые типы информации для функций обратного вызова, передаваемых во все асинхронные AsyncResult методы. Однако то, что возвращается в AsyncResult.value свойство, зависит от функциональности метода Async.

Например, addHandlerAsync методы (объектов Binding, CustomXmlPart, Document, RoamingSettings и Settings ) используются для добавления функций обработчика событий. Свойство AsyncResult.value в этих функциях обратного вызова всегда возвращает undefined, так как при добавлении обработчика событий доступ к данным или объекту не осуществляется.

С другой стороны, при вызове Document.getSelectedDataAsync метода он возвращает данные, выбранные пользователем в документе AsyncResult.value в качестве свойства в обратном вызове. Или при вызове метода Bindings.getAllAsync он возвращает массив всех Binding объектов в документе.

Описание того, что возвращается AsyncResult.value в свойство для Async метода, см. в callback разделе справочного раздела этого метода.

Шаблоны асинхронного программирования

Общие API в API JavaScript для Office поддерживают два типа шаблонов асинхронного программирования.

  • Вложенные обратные вызовы
  • Обещания

Примечание.

В текущей версии API JavaScript для Office встроенная поддержка шаблона обещаний работает только с кодом для привязок в электронных таблицах Excel и Word документах. Однако другие функции, имеющие обратные вызовы, можно поместить в собственную настраиваемую Promiseфункцию возврата. Дополнительные сведения см. в статье Перенос общих API в функции promise-returning.

Асинхронное программирование с использованием вложенных функций обратного вызова

Зачастую для какой-либо задачи необходимо выполнять несколько асинхронных операций. Для этого можно вкладывать один асинхронный вызов в другой.

В следующем примере кода показано, как вложить два асинхронных вызова.

  • Сначала вызывается метод Bindings.getByIdAsync для получения доступа к привязке в документе с именем "MyBinding". Объект AsyncResult , возвращаемый параметру result этого обратного вызова, предоставляет доступ к указанному объекту привязки AsyncResult.value из свойства .
  • Затем объект привязки, доступный из первого result параметра, используется для вызова метода Binding.getDataAsync .
  • Наконец, параметр обратного вызова, result2 переданного методу Binding.getDataAsync , используется для отображения данных в привязке.
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; 
}

Этот базовый вложенный шаблон обратного вызова можно использовать для всех асинхронных методов в общих API.

Асинхронное программирование с применением шаблона, предусматривающего использование обещаний для получения доступа к данным в привязках

Вместо передачи функции обратного вызова и ожидания возврата функции перед продолжением скрипта шаблон программирования promises немедленно возвращает Promise объект, представляющий его предполагаемый результат. Однако в отличие от реального синхронного программирования выполнение обещанного результата фактически откладывается до тех пор, пока среда выполнения надстроек Office не завершит запрос. Если выполнить запрос не удается, используется обработчик onError.

Общие API предоставляют функцию Office.select для поддержки шаблона обещаний при работе с существующими объектами привязки. Объект promise, возвращенный функции, Office.select поддерживает только четыре метода, напрямую доступные из объекта Binding .

Шаблон обещаний для работы с привязками принимает эту форму.

Office.select( selectorExpression,onError).BindingObjectAsyncMethod;

Параметр selectorExpression принимает форму "bindings#bindingId", где bindingId — это имя ( id) привязки, созданной в документе или электронной таблице (с помощью одного из методов Bindings addFrom коллекции: addFromNamedItemAsync, addFromPromptAsyncили addFromSelectionAsync). В примере selectorExpressionbindings#cities указывает, что требуется получить доступ к привязке с идентификатором "cities".

Параметр onError — это функция обработки ошибок, которая принимает один параметр типа AsyncResult. Используется для доступа к объекту, Errorselect если функции не удается получить доступ к указанной привязке. В следующем примере показана базовая функция обработки ошибки, которую можно передать в параметр 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; 
}

Замените заполнитель BindingObjectAsyncMethod вызовом любого из четырех Binding методов объектов, поддерживаемых объектом promise: getDataAsync, setDataAsync, addHandlerAsyncили removeHandlerAsync. Вызовы этих методов не поддерживают дополнительные шаблоны promise. В этом случае необходимо использовать шаблон вложенной функции обратного вызова.

Binding После выполнения обещания объекта его можно повторно использовать в вызове метода с цепочкой, как если бы это была привязка. В случае успешного выполнения надстройки не будет асинхронно повторять выполнение обещания. Binding Если обещание объекта не может быть выполнено, среда выполнения надстройки попытается снова получить доступ к объекту привязки при следующем вызове одного из его асинхронных методов.

В следующем примере функция используется select для получения привязки id с "cities" из Bindings коллекции, а затем вызывается метод addHandlerAsync для добавления обработчика событий для события dataChanged привязки.

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

Важно!

Объектное Binding обещание, возвращаемое функцией Office.select , предоставляет доступ только к четырем Binding методам объекта . Если необходимо получить доступ к любому из других элементов Binding объекта, вместо этого необходимо использовать Document.bindings методы свойства и Bindings.getByIdAsync или Bindings.getAllAsync для получения Binding объекта.

Передача необязательных параметров асинхронным методам

Общий синтаксис для всех асинхронных методов соответствует этому шаблону.

asyncMethod(requiredParameters, [optionalParameters],callbackFunction);

Все асинхронные методы поддерживают необязательные параметры. Они передаются в виде объекта JavaScript. Объект, содержащий необязательные параметры, представляет собой неупорядоченную коллекцию пар "ключ-значение". Можно создать объект, содержащий необязательные встроенные параметры, или путем создания options объекта и передачи его в качестве параметра options .

Передача необязательных параметров в строке

Ниже приведен пример метода Document.setSelectedDataAsync с необязательными параметрами, определенными встроенными. Два необязательных параметра, coercionType и asyncContext, определяются как анонимный объект JavaScript.

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

Передача необязательных параметров в именованном объекте

Кроме того, можно создать именованный объект, который задает необязательные параметры отдельно от вызова метода, а затем передать объект в качестве аргумента options . В следующем примере показан один из способов создания options объекта, где parameter1, value1и т. д. являются заполнителями для фактических имен и значений параметров.

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

Когда указываются параметры ValueFormat и FilterType, код будет таким:

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

Вот еще один способ создания options объекта.

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

Это выглядит как в следующем примере, если используется для указания ValueFormat параметров и FilterType :

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

В следующем примере показано, как вызвать метод путем Document.setSelectedDataAsync указания необязательных параметров в объекте options .

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

В обоих примерах необязательных параметров параметр обратного вызова указывается как последний параметр (после встроенных необязательных параметров или после объекта аргумента options ). Кроме того, можно указать параметр обратного вызова внутри встроенного объекта JavaScript или в объекте options . Однако параметр обратного вызова можно передать только в одном расположении: в options объекте (встроенном или созданном извне) или в качестве последнего параметра, но не в обоих расположениях.

Перенос общих API-интерфейсов в Promiseфункции возврата

Методы Common API (и API Outlook) не возвращают promises. Поэтому вы не можете использовать await для приостановки выполнения до завершения асинхронной операции. Если требуется await поведение, заключите вызов метода в явно созданный Promiseобъект .

Базовый шаблон заключается в создании асинхронного метода, который немедленно возвращает объект Promise и разрешает этот объект Promise после завершения внутреннего метода или отклоняет объект в случае сбоя метода. Ниже приведен простой пример.

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

Если эту функцию необходимо ожидать, ее можно вызвать с await помощью ключевое слово или передать в then функцию.

Примечание.

Этот метод особенно полезен, если необходимо вызвать Общий API внутри вызова run функции в объектной модели приложения. Пример функции, используемой getDocumentFilePath таким образом, см. в файлеHome.js в примере Word-Add-in-JavaScript-MDConversion.

См. также