Office アドインにおける非同期プログラミング
重要
この記事は、Office 2013 で導入された Office JavaScript API モデルである Common API に適用されます。 これらの API には、複数の種類の Office アプリケーション間で共通の UI、ダイアログ、クライアント設定などの機能が含まれます。 Outlook アドインは、共通 API、特に メールボックス オブジェクトを通じて公開される API のサブセットのみを使用します。
共通 API は、アプリケーション固有の API でサポートされていないシナリオにのみ使用してください。 アプリケーション固有の API ではなく共通 API を使用する場合については、「Office JavaScript API について理解する」を参照してください。
Office アドイン API で非同期プログラミングが使用される理由 JavaScript はシングルスレッドの言語であるため、スクリプトで実行時間の長い同期プロセスが呼び出されると、そのプロセスが完了するまで後続のすべてのスクリプト実行がブロックされます。 Office Web クライアント (ただしデスクトップ クライアント) に対する特定の操作は、同期的に実行されると実行をブロックする可能性があるため、ほとんどの Office JavaScript API は非同期的に実行するように設計されています。 これにより、Office アドインの応答性と高速性が確実に向上します。 このような非同期メソッドを利用するときは、多くの場合、コールバック関数の記述も必要です。
API 内のすべての非同期メソッドの名前は、または Item.loadCustomPropertiesAsync
メソッドなどDocument.getSelectedDataAsync
Binding.getDataAsync
、"Async" で終わる。 "Async" メソッドは呼び出されるとすぐに実行され、後続のスクリプトも続けて実行することができます。 "Async" メソッドに渡す任意のコールバック関数は、データまたは要求された操作の準備が整い次第、すぐに実行されます。 コールバック関数の実行は通常、直ちに行われますが、戻るまでに若干の遅延が生じることがあります。
次の図は、サーバー ベースのWordまたは Excel で開いているドキュメントでユーザーが選択したデータを読み取る "Async" メソッドの呼び出しの実行フローを示しています。 "Async" 呼び出しが行われる時点で、JavaScript 実行スレッドは追加のクライアント側処理を自由に実行できます (図には何も表示されません)。 "Async" メソッドが返されると、コールバックはスレッドでの実行を再開し、アドインはデータにアクセスして何かを行い、結果を表示できます。 Windows または Mac で Office クライアント アプリケーションを操作する場合も、同じ非同期実行パターンが保持されます。
リッチ クライアントと Web クライアントの両方でこの非同期設計をサポートすることは、Office アドイン開発モデルの "write once-run cross-platform (一度書けばどんなプラットフォームでも動く)" 設計目的の一部です。 たとえば、Excel on Windows と Excel on the web の両方で実行される単一のコード ベースを使用して、コンテンツアドインまたは作業ウィンドウ アドインを作成できます。
"Async" メソッドのコールバック関数を記述する
コールバック引数として "Async" メソッドに渡す コールバック 関数では、コールバック関数の実行時に AsyncResult オブジェクトへのアクセスを提供するためにアドイン ランタイムが使用する 1 つのパラメーターを宣言する必要があります。 次のように記述することができます。
"Async" メソッドの コールバック パラメーターとして "Async" メソッドの呼び出しに沿って直接書き込んで渡す必要がある匿名関数。
名前付き関数。その関数の名前を "Async" メソッドの コールバック パラメーターとして渡します。
匿名関数は、コードを 1 回だけ使用する場合に便利です。名前がないため、コードの別の部分で参照することはできません。 名前付き関数は、コールバック関数を複数の "Async" メソッドに再利用する場合に便利です。
匿名コールバック関数を記述する
次の匿名コールバック関数は、コールバックが返されるときに AsyncResult.value プロパティからデータを取得する という名前result
の 1 つのパラメーターを宣言します。
function (result) {
write('Selected data: ' + result.value);
}
次の例は、完全な "Async" メソッド呼び出しのコンテキストで、この匿名コールバック関数を行に渡す方法を Document.getSelectedDataAsync
示しています。
最初の coercionType 引数 は、
Office.CoercionType.Text
選択したデータを文字列として返すように指定します。2 番目の コールバック 引数は、 メソッドにインラインで渡される匿名関数です。 関数は、実行時に result パラメーターを使用してオブジェクトの
AsyncResult
プロパティにアクセスvalue
し、ユーザーがドキュメント内で選択したデータを表示します。
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
という名前の関数を callback パラメーターとして渡すように書き換えることができます。
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 プロパティに返される内容の違い
オブジェクトの AsyncResult
プロパティstatus
、、および error
プロパティはasyncContext
、すべての "Async" メソッドに渡されるコールバック関数に同じ種類の情報を返します。 ただし、プロパティに AsyncResult.value
返されるものは、"Async" メソッドの機能によって異なります。
たとえば、 addHandlerAsync
( Binding、 CustomXmlPart、 Document、 RoamingSettings、 Settings オブジェクトの) メソッドを使用して、これらのオブジェクトで表される項目にイベント ハンドラー関数を追加します。 任意のAsyncResult.value
addHandlerAsync
メソッドに渡すコールバック関数からプロパティにアクセスできますが、イベント ハンドラーを追加するときにデータまたはオブジェクトがアクセスされないので、アクセスしようとすると、value
プロパティは常に undefined を返します。
一方、 メソッドを Document.getSelectedDataAsync
呼び出すと、ドキュメントで選択したデータがコールバックのプロパティに AsyncResult.value
返されます。 または、 Bindings.getAllAsync メソッドを呼び出すと、ドキュメント内のすべてのオブジェクトの配列が Binding
返されます。 Bindings.getByIdAsync メソッドを呼び出すと、1 つのBinding
オブジェクトが返されます。
メソッドの プロパティAsync
にAsyncResult.value
返される内容の説明については、そのメソッドのリファレンス トピックの「コールバック値」セクションを参照してください。 メソッドを提供 Async
するすべてのオブジェクトの概要については、 AsyncResult オブジェクトに関するトピックの下部にある表を参照してください。
非同期プログラミング パターン
Office JavaScript API では、2 種類の非同期プログラミング パターンがサポートされています。
- 入れ子のコールバックの使用
- promise パターンの使用
コールバック関数のある非同期プログラミングでは、多くの場合、2 つ以上のコールバック内に 1 つのコールバックで返された結果を入れ子にすることが必要となります。 その場合、API のすべての "Async" メソッドからの入れ子のコールバックを使用できます。
入れ子のコールバックを使用することは、ほとんどの JavaScript 開発者にとってなじみのあるプログラミング パターンですが、コールバックが深い入れ子になっているコードは読みにくく、理解しにくいものです。 入れ子になったコールバックの代わりに、Office JavaScript API では promises パターンの実装もサポートされています。
注:
現在のバージョンの Office JavaScript API では、promises パターンの組み込みサポートは、Excel スプレッドシートとWord ドキュメントのバインドのコードでのみ機能します。 ただし、独自のカスタム Promise を返す関数内にコールバックがある他の関数をラップできます。 詳細については、「 Promise を返す関数で共通 API をラップする」を参照してください。
入れ子のコールバック関数を使用する非同期プログラミング
多くの場合、タスクを完了するには、2 つ以上の非同期操作を実行する必要があります。 これを実現するために、1 つの "Async" 呼び出し内で別の呼び出しを入れ子にできます。
次のコード例では、2 つの非同期呼び出しを入れ子にしています。
- 最初に、Bindings.getByIdAsync メソッドが呼び出され、"MyBinding" という名前のドキュメントのバインドにアクセスします。 そのコールバックのパラメーターに
result
返される オブジェクトはAsyncResult
、 プロパティから指定されたバインド オブジェクトへのアクセスを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;
}
この基本的な入れ子になったコールバック パターンは、Office JavaScript API のすべての非同期メソッドに使用できます。
次のセクションでは、非同期メソッドの入れ子のコールバックで匿名関数または名前付き関数を使用する方法を示します。
入れ子になったコールバックに匿名関数を使用する
次の例では、2 つの匿名関数がインラインで宣言され、 メソッドと getDataAsync
メソッドにgetByIdAsync
入れ子になったコールバックとして渡されます。 関数は単純でインラインのため、実装の意図は明白です。
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;
}
入れ子になったコールバックに名前付き関数を使用する
複雑な実装の場合、名前付き関数を使用すると、読みやすく、保守管理がしやすく、再利用しやすくなります。 次の例では、前のセクションの例の 2 つの匿名関数を、 と showResult
という名前deleteAllData
の関数として書き換えています。 これらの名前付き関数は、 メソッドと deleteAllDataValuesAsync
メソッドにgetByIdAsync
名前によってコールバックとして渡されます。
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;
}
promise パターンを使用してバインドのデータにアクセスする非同期プログラミング
コールバック関数を渡し、その関数が戻るのを待ってから実行を続行する代わりに、promise プログラミング パターンを使用すれば、その意図した結果を表す promise オブジェクトがすぐに返されます。 ただし、本物の同期プログラミングとは異なり、実際には Office アドインのランタイム環境が要求を完了できるまでは、約束された結果の履行は実際には延期されます。 要求が履行されない状況に対処するために onError ハンドラーが用意されています。
Office JavaScript API には、既存のバインド オブジェクトを操作するための promises パターンをサポートする Office.select 関数が用意されています。 関数に Office.select
返される promise オブジェクトでは、 Binding オブジェクトから直接アクセスできる 4 つのメソッド ( getDataAsync、 setDataAsync、 addHandlerAsync、 removeHandlerAsync) のみがサポートされています。
バインドを操作するための promises パターンは、この形式になります。
Office.select(selectorExpression, onError).BindingObjectAsyncMethod
selectorExpression パラメーターは、 という形式"bindings#bindingId"
をとります。ここで、bindingId は、ドキュメントまたはスプレッドシートで以前に作成したバインドの名前 ( id
) です (コレクションの "addFrom" メソッドBindings
の 1 つを使用します: addFromNamedItemAsync
、addFromPromptAsync
、または addFromSelectionAsync
)。 たとえば、セレクター式 bindings#cities
は、"cities" の ID を使用してバインドにアクセスすることを指定します。
onError パラメーターは、指定したバインドへのアクセスに関数が失敗した場合select
に、オブジェクトへのアクセスError
に使用できる型AsyncResult
の 1 つのパラメーターを受け取るエラー処理関数です。 次の例は、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 オブジェクトgetDataAsync
でサポートされている 4 つのBinding
オブジェクト メソッド (、、setDataAsync
addHandlerAsync
、または removeHandlerAsync
) の呼び出しに置き換えます。 これらのメソッドの呼び出しでは追加の promise がサポートされません。 これらは入れ子のコールバック関数パターンを使用して呼び出す必要があります。
オブジェクト promise Binding
が満たされた後は、バインディングであるかのように、チェーンされたメソッド呼び出しで再利用できます (アドイン ランタイムは、promise の実行を非同期的に再試行しません)。 オブジェクトの promise を Binding
満たせない場合、アドイン ランタイムは、次にその非同期メソッドの 1 つが呼び出されたときに、バインド オブジェクトへのアクセスを再試行します。
次のコード例では、 関数をselect
使用してコレクションから Bindings
"cities
" を使用id
してバインドを取得し、addHandlerAsync メソッドを呼び出して、バインドの dataChanged イベントのイベント ハンドラーを追加します。
function addBindingDataChangedEventHandler() {
Office.select("bindings#cities", function onError(){/* error handling code */}).addHandlerAsync(Office.EventType.BindingDataChanged,
function (eventArgs) {
doSomethingWithBinding(eventArgs.binding);
});
}
重要
関数によってOffice.select
返されるオブジェクト promise はBinding
、オブジェクトの 4 つのメソッドBinding
にのみアクセスできます。 オブジェクトの他のメンバーBinding
のいずれかにアクセスする必要がある場合は、代わりに プロパティまたは Bindings.getByIdAsync
Bindings.getAllAsync
メソッドをDocument.bindings
使用してオブジェクトを取得するBinding
必要があります。 たとえば、オブジェクトのBinding
プロパティ (document
、、id
または プロパティ) にアクセスする必要がある場合、または MatrixBinding オブジェクトまたはtype
TableBinding オブジェクトのプロパティにアクセスする必要がある場合は、 または getAllAsync
メソッドを使用getByIdAsync
してオブジェクトをBinding
取得する必要があります。
省略可能なパラメーターを非同期メソッドに渡す
すべての "Async" メソッドの一般的な構文は、このパターンに従います。
AsyncMethod(
RequiredParameters, [
OptionalParameters],
CallbackFunction);
すべての非同期メソッドは、1 つ以上の省略可能なパラメーターを含む JavaScript オブジェクトとして渡される省略可能なパラメーターをサポートします。 省略可能なパラメーターを含むオブジェクトは、キーと値を区切る ":" 文字を持つキーと値のペアの順序なしコレクションです。 オブジェクト内の各ペアはコンマで区切られ、ペアのセット全体が中かっこで囲まれます。 キーはパラメーター名であり、値はそのパラメーターに渡す値です。
オプションのパラメーターを含むオブジェクトをインラインで作成することも、オブジェクトを options
作成して options パラメーターとして渡すこともできます。
省略可能なパラメーターをインラインで渡す
たとえば、オプションのパラメーターをインラインで指定して Document.setSelectedDataAsync メソッドを呼び出す場合の構文は、次のようになります。
Office.context.document.setSelectedDataAsync(data, {coercionType: 'coercionType', asyncContext: 'asyncContext'},callback);
呼び出し元の構文のこの形式では、 coercionType と asyncContext の 2 つの省略可能なパラメーターは、かっこで囲まれた匿名 JavaScript オブジェクトとしてインラインで定義されます。
次の例は、省略可能なパラメーターをインラインで指定して メソッド Document.setSelectedDataAsync
を呼び出す方法を示しています。
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
のオブジェクトを作成し、そのオブジェクトを options
options 引数として渡すこともできます。
次の例は、オブジェクトを作成する 1 つの方法を 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;
と FilterType
パラメーターを指定するために使用した場合、次のValueFormat
例のようになります。
const options = {};
options["ValueFormat"] = "unformatted";
options["FilterType"] = "all";
注:
オブジェクトを作成するいずれかの方法を使用する options
場合は、名前が正しく指定されている限り、任意の順序で省略可能なパラメーターを指定できます。
次の例は、オブジェクトで省略可能なパラメーターを 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;
}
どちらのオプションのパラメーター例でも、callback パラメーターが最後のパラメーターとして (インラインのオプションのパラメーターまたは options 引数オブジェクトに続けて) 指定されています。 または、インライン JavaScript オブジェクト内または オブジェクト内で コールバック パラメーターを options
指定することもできます。 ただし、 コールバック パラメーターは、オブジェクト (インラインまたは外部で作成) 内 options
の 1 つの場所、または最後のパラメーターとして渡すことができますが、両方を渡すわけではありません。
Promise を返す関数で共通 API をラップする
Common API (および Outlook API) メソッドは Promise を返しません。 そのため、非同期操作が完了するまで 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
渡すことができます。
注:
この手法は、アプリケーション固有のオブジェクト モデルで関数の run
呼び出し内で Common API を呼び出す必要がある場合に特に便利です。 この方法で使用されている関数のgetDocumentFilePath
例については、サンプル Word-Add-in-JavaScript-MDConversion のファイルHome.js を参照してください。
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);
}
});
}
});
});
}
関連項目
Office Add-ins
フィードバック
https://aka.ms/ContentUserFeedback」を参照してください。
以下は間もなく提供いたします。2024 年を通じて、コンテンツのフィードバック メカニズムとして GitHub の issue を段階的に廃止し、新しいフィードバック システムに置き換えます。 詳細については、「フィードバックの送信と表示