次のようなコードを見かけました:
myObj.doSome("task").then(function(env) {
// logic
});
どこthen()
から来たのですか?
ベストアンサー1
JavaScript で非同期呼び出しを処理する従来の方法は、コールバックを使用することです。アプリケーションをセットアップするために、サーバーに 3 回連続して呼び出しを行う必要があるとします。コールバックを使用すると、コードは次のようになります (サーバー呼び出しを行う xhrGET 関数を想定)。
// Fetch some server configuration
xhrGET('/api/server-config', function(config) {
// Fetch the user information, if he's logged in
xhrGET('/api/' + config.USER_END_POINT, function(user) {
// Fetch the items for the user
xhrGET('/api/' + user.id + '/items', function(items) {
// Actually display the items here
});
});
});
この例では、まずサーバー構成を取得します。次に、それに基づいて現在のユーザーに関する情報を取得し、最後に現在のユーザーのアイテムのリストを取得します。各 xhrGET 呼び出しは、サーバーが応答したときに実行されるコールバック関数を受け取ります。
もちろん、ネストのレベルが高ければ高いほど、コードの読み取り、デバッグ、保守、アップグレード、および基本的に操作が難しくなります。これは一般にコールバック地獄として知られています。また、エラーを処理する必要がある場合は、エラーが発生した場合に何を行う必要があるかを指示するために、各 xhrGET 呼び出しに別の関数を渡す必要がある可能性があります。共通のエラー ハンドラーを 1 つだけ用意したい場合は、それは不可能です。
Promise API は、このネストの問題とエラー処理の問題を解決するために設計されました。
Promise API は以下を提案します。
- 各非同期タスクは
promise
オブジェクトを返します。 - 各オブジェクトには、ハンドラーとハンドラーの2 つの引数を取ることができる関数
promise
があります。then
success
error
- 関数内の成功またはエラー ハンドラは、非同期タスクが終了した後に1 回
then
だけ呼び出されます。 - この関数は、複数の呼び出しを連鎖できるように
then
も返します。promise
- 各ハンドラ (成功またはエラー) は を返すことができ、これはのチェーン内の
value
次の関数に として渡されます。argument
promise
- ハンドラーが を返す場合
promise
(別の非同期要求を行う)、次のハンドラー (成功またはエラー) はその要求が完了した後にのみ呼び出されます。
$http
したがって、前のサンプル コードは、Promise とサービス (AngularJs 内)を使用して、次のように変換できます。
$http.get('/api/server-config').then(
function(configResponse) {
return $http.get('/api/' + configResponse.data.USER_END_POINT);
}
).then(
function(userResponse) {
return $http.get('/api/' + userResponse.data.id + '/items');
}
).then(
function(itemResponse) {
// Display items here
},
function(error) {
// Common error handling
}
);
成功とエラーの伝播
プロミスのチェーン化は非常に強力なテクニックであり、サービスにサーバー呼び出しを行わせ、データの後処理を行ってから、処理済みのデータをコントローラーに返すなど、多くの機能を実現できます。ただし、promise
チェーンを使用する場合は、いくつか留意すべき点があります。
promise
P1、P2、P3 という 3 つのプロミスを含む次の仮想チェーンについて考えてみましょう。それぞれにpromise
成功ハンドラーとエラー ハンドラーがあるため、P1 には S1 と E1、P2 には S2 と E2、P3 には S3 と E3 があります。
xhrCall()
.then(S1, E1) //P1
.then(S2, E2) //P2
.then(S3, E3) //P3
エラーのない通常のフローでは、アプリケーションは S1、S2、そして最後に S3 の順に流れます。しかし、現実には物事はそれほどスムーズには進みません。P1 でエラーが発生したり、P2 でエラーが発生して E1 または E2 がトリガーされたりすることがあります。
次のケースを考えてみましょう。
• P1 でサーバーから正常な応答を受け取りましたが、返されたデータが正しくないか、サーバー上に利用可能なデータがありません (空の配列を考えてください)。このような場合、次のプロミス P2 では、エラー ハンドラー E2 をトリガーする必要があります。
• プロミス P2 のエラーが発生し、E2 がトリガーされます。ただし、ハンドラー内にはキャッシュからのデータがあり、アプリケーションを通常どおりロードできることが保証されています。その場合、E2 の後に S3 が呼び出されるようにする必要があります。
したがって、成功ハンドラーまたはエラーハンドラーを記述するたびに、現在の関数を前提として、このプロミスはプロミス チェーンの次のハンドラーにとって成功か失敗かを呼び出す必要があります。
チェーン内の次のプロミスの成功ハンドラをトリガーしたい場合は、成功ハンドラまたはエラーハンドラから値を返すだけです。
一方、チェーン内の次のプロミスのエラーハンドラをトリガーしたい場合は、deferred
オブジェクトを使用してそのreject()
メソッドを呼び出すことができます。
さて、遅延オブジェクトとは何でしょうか?
jQuery の遅延オブジェクトは、通常は非同期で後で完了する作業単位を表します。作業単位が完了すると、オブジェクトは
deferred
解決済みまたは失敗に設定できます。オブジェクトにはオブジェクト
deferred
が含まれていますpromise
。promise
オブジェクトを介して、作業単位が完了したときに何が起こるかを指定できます。これを行うには、promise
オブジェクトにコールバック関数を設定します。
JQuery の遅延オブジェクト:https://api.jquery.com/jquery.deferred/
AngularJs の遅延オブジェクト:https://docs.angularjs.org/api/ng/service/$q