Office 加载项中的异步编程
重要
本文适用于 常见 API,即 Office 2013 中引入的 Office JavaScript API 模型。 这些 API 包括在多种类型的 Office 应用程序中都很常见的 UI、对话框和客户端设置等功能。 Outlook 加载项仅使用通用 API,尤其是通过邮箱对象公开的 API 子集。
对于特定于应用程序的 API 不支持的场景,应仅使用通用 API。 若要了解何时使用通用 API(而非特定于应用程序的 API),请参阅了解 Office JavaScript API。
为什么 Office 外接程序 API 使用异步编程? JavaScript 是单线程语言。 如果脚本调用 Office 客户端长时间运行的同步进程,则会阻止所有后续脚本,直到该进程完成。 异步可确保 Office 外接程序具有响应性和快速性。
通用 API 中所有异步方法的名称以“Async”结尾,例如 Document.getSelectedDataAsync
、 Binding.getDataAsync
或 Item.loadCustomPropertiesAsync
方法。 调用“Async”方法时,它会立即运行。 在客户端完成操作时,脚本的其余部分会继续。 传递给“Async”方法的可选回调函数在数据或请求的操作准备就绪后立即运行。 这通常立即发生,但可能会稍有延迟。
下图显示了“Async”方法的流,该方法读取用户在文档中选择的数据。 进行“异步”调用时,JavaScript 线程可以自由地执行任何其他客户端处理 (尽管关系图) 中未显示任何内容。 当“Async”方法返回时,回调将在线程上恢复。 然后,加载项可以访问数据、执行某些操作并显示结果。 跨平台的模式相同。
为“异步”方法编写回调函数
作为回调参数传递给“Async”方法的 回调 函数必须声明单个参数。 外接程序运行时使用该参数为回调函数提供对 AsyncResult 对象的访问权限。
回调函数可以是匿名函数或命名函数。 如果您打算只使用一次代码,则可以使用匿名函数,这是因为该函数没有名称,您不能在代码的其他部分引用此代码。 如果您打算重复将回调函数用于多个"Async"方法,则可以使用命名函数。
编写匿名回调函数
以下匿名回调函数为客户端返回的数据声明名为 result
的单个参数。 当回调返回时,它会从 AsyncResult.value 属性检索和写入该数据。
function (result) {
write('Selected data: ' + result.value);
}
以下示例在对 方法的完整“异步”方法调用 Document.getSelectedDataAsync(coercionType, callback)
的上下文中演示此匿名回调函数。
第一个 coercionType 参数
Office.CoercionType.Text
指定将所选数据作为文本字符串返回。第二个 回调 参数是内联传递给 方法的匿名函数。 当函数运行时,它使用 result 参数来访问
value
对象的 属性AsyncResult
。 然后,它显示用户在文档中选择的数据。
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”方法的功能而异。
例如,Binding、addHandlerAsync
CustomXmlPart、Document、RoamingSettings 和 Settings 对象的方法 () 用于添加事件处理程序函数。
AsyncResult.value
这些回调函数中的 属性始终返回未定义,因为在添加事件处理程序时,不会访问任何数据或对象。
另一方面,如果调用 Document.getSelectedDataAsync
方法,它将返回用户在文档中选择的数据作为 AsyncResult.value
回调中的 属性。 或者,如果调用 Bindings.getAllAsync 方法,它将返回文档中所有 Binding
对象的数组。
有关返回到 AsyncResult.value
方法属性 Async
的内容的说明,请参阅 callback
该方法的参考主题部分。
异步编程模式
Office JavaScript API 中的常见 API 支持两种类型的异步编程模式。
- 嵌套回调
- 承诺
注意
在当前版本的 Office JavaScript API 中,对 promises 模式的内置支持仅适用于 Excel 电子表格和Word文档中绑定的代码。 但是,可以在自己的自定义 Promise
返回函数中包装具有回调的其他函数。 有关详细信息,请参阅 在 Promise-returning 函数中包装通用 API。
使用嵌套回调函数的异步编程
通常,完成一项任务需要执行两个或更多个异步操作。 为实现此目的,可在一个调用中嵌套另一个"Async"调用。
以下代码示例内嵌两个异步调用。
- 首先,调用 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 函数,用于在处理现有绑定对象时支持 promises 模式。 返回到函数的 Office.select
promise 对象仅支持从 Binding 对象直接访问的四种方法。
用于处理绑定的承诺模式采用此形式。
Office.select(
selectorExpression,
onError).
BindingObjectAsyncMethod;
selectorExpression 参数采用 的形式"bindings#bindingId"
,其中 bindingId 是你在文档或电子表格中创建的绑定的名称 id
() , (集合的“addFrom”方法Bindings
之一:addFromNamedItemAsync
、 addFromPromptAsync
或 addFromSelectionAsync
) 。 的示例 selectorExpressionbindings#cities
指定你希望访问 ID 为“cities”的绑定。
onError 参数是一个错误处理函数,它采用 类型的AsyncResult
单个参数。 如果函数无法访问指定的绑定,select
则用于Error
访问对象。 以下示例显示了一个可传递给 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 占位符替换为对 promise 对象支持的四 Binding
个对象方法中的任何一个的调用: getDataAsync
、 setDataAsync
、 addHandlerAsync
或 removeHandlerAsync
。 对这些方法的调用不支持其他的承诺。 在这种情况下,必须使用 嵌套回调函数模式。
Binding
实现对象承诺后,可以在链接的方法调用中重复使用它,就像它是绑定一样。 如果成功,外接程序运行时不会异步重试履行承诺。
Binding
如果无法实现对象承诺,外接程序运行时将在下次调用其异步方法之一时再次尝试访问绑定对象。
以下示例使用 select
函数从Bindings
集合中id
检索具有“”cities
的绑定,然后调用 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";
以下示例演示如何通过在 对象中options
指定可选参数来Document.setSelectedDataAsync
调用 方法。
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
对象中 (内联或) 外部创建,或作为最后一个参数传递,但不能同时传递这两个位置。
在 返回函数中 Promise
包装通用 API
通用 API (和 Outlook API) 方法不返回 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));
}
})
}
当需要等待此函数时,可以使用 关键字 (keyword) 调用await
它,也可以将其传递给then
函数。
注意
当需要在应用程序特定对象模型中的函数调用内部调用通用 API 时, run
此方法特别有用。 有关以这种方式使用的函数的示例getDocumentFilePath
,请参阅示例 Word-Add-in-JavaScript-MDConversion 中的文件Home.js。