WinJS.xhr().then は必要なプロミスを返すため、コード内で新しい WinJS.Promise を作成する必要はありません。背景を説明すると、完了したハンドラーを promise にアタッチするには、.then と .done の 2 つの方法があります。どちらも同じ引数を取りますが、戻り値が異なります。.done は、promise チェーンの最後で使用することを意図しているため、undefined を返します。
一方、.then は、完了 (またはエラー ハンドラー) が返されたときに満たされる promise を返します。フルフィルメント値は、完了ハンドラー (またはエラー ハンドラー) から返された値です。
(ちなみに、私はこのような問題を明確にするために、promise についてさらに多くのことを書いています。短いバージョンはAll about promises (Windows Dev Blog) にあります。より完全なバージョンは、付録 A「Demystifying Promises」にあります。私の無料の電子ブック、Programming Windows Store Apps in HTML, CSS, and JavaScript, Second Editionの "は、現在 2 番目のプレビュー段階にあります)。
独自の非同期関数を作成する場合、他の既存の非同期関数 (WinJS.xhr など) を呼び出すときに使用する最適なパターンは、その .then から promise を返すことです。したがって、あなたの場合、 getAccessTokenAsync を次のようにします。
getAccessTokenAsync = function (username, password) {
var serializedData = {
username: username, password: password,
};
return WinJS.xhr({
type: "post",
url: "http://127.0.0.1:8080/authenticate/login",
responseType: "json",
data: JSON.stringify(serializedData)
}).then(
function complete(result){
return JSON.parse(result.responseText);
}
);
})
}
これは、トークンに割り当てた Promise を返します。そのフルフィルメント値は、JSON.parse(result.responseText) の結果になります。
new WinJS.Promise の最初の使用法が間違っている理由を説明しましょう。ここでコンストラクターに与える関数引数自体は、3 つの引数を受け取ります。各引数は別の関数であり、それぞれを「ディスパッチャー」と呼んでおり、完了、エラー、および進行状況に対して 1 つ取得します。promise 内のコードの本体は、適切なイベントでこれらのディスパッチャーを呼び出します。
次に、これらのディスパッチャーは、promise の .then または .done を通じてサブスクライブされた関数の完了、エラー、および進行ハンドラーを呼び出します。つまり、これらのディスパッチャーを呼び出すことが、実際にこれらのハンドラーの呼び出しをトリガーする唯一の方法です。
元のコードでは、これらを実際に呼び出すことはありません。WinJS.Promise コンストラクターが完成したディスパッチャーにのみ注意を払うようにすることで、これをシンプルに保ちました。ただし、WinJS.xhr の呼び出しが完了すると、このディスパッチャーは呼び出されません。混乱の一部は、complete という引数があり、WinJS.xhr().done の完了ハンドラーにも "complete" という名前を付けていることです。最後の JSON.parse 呼び出しにブレークポイントを設定すると、ヒットするはずですが、返された値は完全なディスパッチャーに渡されないため、飲み込まれてしまいます。
これを修正するには、元のコードを次のようにします。
return new WinJS.Promise(function (completeDispatch) { //Name the dispatcher for clarity
WinJS.xhr({
type: "post",
url: "http://127.0.0.1:8080/authenticate/login",
responseType: "json",
data: JSON.stringify(serializedData)
}).done(
function (result) { //Keep this anonymous for clarity
completeDispatch(JSON.parse(result.responseText));
}
);
})
これもうまくいくはずです。ただし、別のプロミス ラッパーはまったく必要ないため、最初に述べたように、WinJS.xhr().then() からプロミスを返すのが最も簡単です。
これらの変更のいずれかにより、getTokenExpiryAsync 内に完了したハンドラーへの呼び出しが表示されるはずです。
コードの他の部分について話しましょう。まず、トークンはエラー状態であっても常に promise に設定されるため、getTokenExpiryAsync 内で null ケースが表示されることはありません。第 2 に、新しい WinJS.Promise コードを上記のように使用すると、errorDispatcher または progressDispatcher を呼び出すことがないため、エラーまたは進行状況が表示されることはありません。これは、代わりに WinJS.xhr().then() からの戻り値を使用するもう 1 つの正当な理由です。
したがって、ここでエラー処理をもう少し詳しく考える必要があります。正確には、有効期限のために new Date() を呼び出したい場合は何ですか? xhr 呼び出しが失敗したとき、または成功した呼び出しからの応答が空を返したときにこれを行いますか?
エラーを処理する 1 つの方法は、上記の新しい WinJS.Promise バリアントを WinJS.xhr().done() と共に使用することです。この場合、エラー ハンドラを .done にサブスクライブします。そのエラー ハンドラで、completeDispather(new Date()); を呼び出して、エラーを伝播するか、新しい Date でラッパーの promise を引き続き実行するかを決定します。その他のエラーについては、errorDispatcher を呼び出します。(これはすべて、成功した xhr 応答に new Date() と同じ形式のデータが含まれていることを前提としていることに注意してください。そうでない場合は、データ値が混在していて、応答全体を返すのではなく、応答から日付を解析する必要があります。)
return new WinJS.Promise(function (completeDispatch) { //Name the dispatcher for clarity
WinJS.xhr({
type: "post",
url: "http://127.0.0.1:8080/authenticate/login",
responseType: "json",
data: JSON.stringify(serializedData)
}).done(
function (result) { //Keep this anonymous for clarity
completeDispatch(JSON.parse(result.responseText));
},
function (e) {
completeDispatch(new Date()); //Turns an xhr error into success with a default.
}
);
})
今説明したことは、コア操作でエラーをキャッチし、デフォルト値を挿入するための本当に良い方法です。これは、あなたが意図していると私は信じています。
一方、WinJS.xhr().then() からの戻り値を使用する場合 (最初のコード バリアント)、getTokenExpiryAsync 内にこのロジックをさらに配置する必要があります。(ちなみに、このコードは、ご覧のように同期的であり、1 つのコード パスが新しい Date を返し、もう 1 つのコード パスが undefined を返すため、必要なものとはまったく異なります。)
トークン自体がプロミスであるため、この getTokenExpiryAsync 自体が非同期である必要があり、したがって有効期限のプロミスも返す必要があります。書き方は次のとおりです。
function getTokenExpiryAsync (token) { //I'd pass token as an argument here
return token.then(
function complete(result) {
return result; //Or parse the date from the original response.
},
function error(e) {
return new Date();
}
);
}
そして、呼び出しコードで次のように言う必要があります。
getTokenExpiryAsync(token).then(function (expiry) {
lastTokenTime = expiry;
}
ここでも、完了値またはエラー メソッドから返された値を履行値とする別の promise である then の戻り値を利用しています。トークンがエラー状態 (WinJS.xhr が失敗した) の場合、.then を呼び出すとエラー ハンドラーが呼び出され、そこで目的の既定値が返されます。それ以外の場合は、応答から必要な有効期限を返します。いずれにせよ、元の呼び出しコードの .then からこの promise から日付を取得します。
少し混乱するかもしれませんが、これは Promises/A 仕様と非同期コーディングの性質であり、特に WinJS ではありません。
これがあなたの報奨金に値することを願っています。:)