[解決済み] AngularJS : プロミスってどこで使うの?
質問
Facebookのログインサービスで 約束 を使ってFB Graph APIにアクセスしている例をいくつか見ました。
例1 :
this.api = function(item) {
var deferred = $q.defer();
if (item) {
facebook.FB.api('/' + item, function (result) {
$rootScope.$apply(function () {
if (angular.isUndefined(result.error)) {
deferred.resolve(result);
} else {
deferred.reject(result.error);
}
});
});
}
return deferred.promise;
}
を使用したサービスも
"$scope.$digest() // Manual scope evaluation"
を使ったサービスは
例2 :
angular.module('HomePageModule', []).factory('facebookConnect', function() {
return new function() {
this.askFacebookForAuthentication = function(fail, success) {
FB.login(function(response) {
if (response.authResponse) {
FB.api('/me', success);
} else {
fail('User cancelled login or did not fully authorize.');
}
});
}
}
});
function ConnectCtrl(facebookConnect, $scope, $resource) {
$scope.user = {}
$scope.error = null;
$scope.registerWithFacebook = function() {
facebookConnect.askFacebookForAuthentication(
function(reason) { // fail
$scope.error = reason;
}, function(user) { // success
$scope.user = user
$scope.$digest() // Manual scope evaluation
});
}
}
質問は
- は何ですか? 違い は何ですか?
- は何ですか? の理由 と の ケース を使用することで $q サービスを利用できますか?
- また、どのように は働きます。 ?
どのように解決するのですか?
これはあなたの質問に対する完全な答えになるわけではありませんが、これがあなたや他の人が
$q
サービスのドキュメントを読もうとしたときに役立つことを願っています。 私はそれを理解するのに時間がかかりました。
AngularJSはちょっと置いておいて、Facebook APIの呼び出しについてだけ考えてみましょう。 どちらのAPIコールも コールバック メカニズムを使用して、Facebookからの応答が利用可能になったときに呼び出し元を通知します。
facebook.FB.api('/' + item, function (result) {
if (result.error) {
// handle error
} else {
// handle success
}
});
// program continues while request is pending
...
JavaScriptなどで非同期処理を行う際の標準的なパターンです。
このパターンの1つの大きな問題は、一連の非同期操作を実行する必要があるときに発生し、連続する各操作は前の操作の結果に依存することになります。 このコードはそれを行っているのです。
FB.login(function(response) {
if (response.authResponse) {
FB.api('/me', success);
} else {
fail('User cancelled login or did not fully authorize.');
}
});
まずログインを試み、ログインが成功したことを確認してから、Graph APIにリクエストを行います。
この場合、2つの処理を連鎖させているだけなのですが、これがなかなか厄介なことになっています。 メソッド
askFacebookForAuthentication
は失敗と成功のコールバックを受け取ります。
FB.login
は成功するが
FB.api
は失敗するのか? このメソッドは常に
success
の結果に関係なく、コールバックを呼び出します。
FB.api
メソッドの結果に関わらず
ここで、3つ以上の非同期操作の堅牢なシーケンスを、各ステップで適切にエラーを処理し、数週間後に他の人やあなたにとってさえ読みやすくなる方法でコーディングしようとしていると想像してみてください。 可能ですが、コールバックをネストし続けるだけで、途中でエラーを見失うことは非常に簡単です。
さて、Facebook APIを少し脇に置いて、Angular Promises APIを考えてみましょう。
$q
サービスによって実装されています。 このサービスで実装されているパターンは、非同期プログラミングを一連の単純なステートメントに似たものに戻す試みで、途中の任意のステップでエラーを「スロー」して最後にそれを処理する機能があり、意味的にはおなじみの
try/catch
ブロックにセマンティックに似ています。
次のような例を考えてみましょう。 2つの関数があり、2つ目の関数が1つ目の関数の結果を消費するとします。
var firstFn = function(param) {
// do something with param
return 'firstResult';
};
var secondFn = function(param) {
// do something with param
return 'secondResult';
};
secondFn(firstFn());
ここで、firstFnとsecondFnの両方が完了するのに長い時間を要すると仮定し、このシーケンスを非同期に処理したいと思います。 まず、新しい
deferred
オブジェクトを作成します。このオブジェクトは操作の連鎖を表します。
var deferred = $q.defer();
var promise = deferred.promise;
は
promise
プロパティはチェーンの最終的な結果を表します。 作成直後のプロミスをログに記録すると、単なる空のオブジェクト(
{}
). まだ何も見るべきものはありません、すぐに移動してください。
これまでのところ、私たちのプロミスはチェーンの出発点を表しているだけです。 では、2つの操作を追加してみましょう。
promise = promise.then(firstFn).then(secondFn);
は
then
メソッドは、チェーンにステップを追加し、拡張されたチェーンの最終的な結果を表す新しいプロミスを返します。 好きなだけステップを追加することができます。
ここまでで、関数の連鎖を設定しましたが、実際には何も起こっていません。 物事を始めるには
deferred.resolve
を呼び出し、チェーンの最初のステップに渡したい初期値を指定します。
deferred.resolve('initial value');
そして...まだ何も起こりません。 モデルの変更が適切に観察されるように、Angularはチェーンの最初のステップを次回まで実際に呼びません。
$apply
が呼び出されるまで、チェーンの最初のステップを実際に呼び出すことはありません。
deferred.resolve('initial value');
$rootScope.$apply();
// or
$rootScope.$apply(function() {
deferred.resolve('initial value');
});
では、エラー処理についてはどうでしょうか。 今までのところ、私たちは
成功ハンドラ
を指定しました。
then
はオプションの第2引数としてエラーハンドラも受け取ります。 もう一つの長いプロミス・チェーンの例です。今回はエラー処理付きです。
var firstFn = function(param) {
// do something with param
if (param == 'bad value') {
return $q.reject('invalid value');
} else {
return 'firstResult';
}
};
var secondFn = function(param) {
// do something with param
if (param == 'bad value') {
return $q.reject('invalid value');
} else {
return 'secondResult';
}
};
var thirdFn = function(param) {
// do something with param
return 'thirdResult';
};
var errorFn = function(message) {
// handle error
};
var deferred = $q.defer();
var promise = deferred.promise.then(firstFn).then(secondFn).then(thirdFn, errorFn);
この例でわかるように、チェーン内の各ハンドラは、トラフィックを次の エラー ハンドラではなく、次の 成功 ハンドラに置き換えます。 ほとんどの場合、チェーンの末端に単一のエラーハンドラを持つことができますが、回復を試みる中間エラーハンドラを持つこともできます。
あなたの例(と質問)に素早く戻るために、私は、それらがFacebookのコールバック指向のAPIをAngularのモデル変更を観察する方法に適応させる2つの異なる方法を表しているとだけ言っておきます。 最初の例は、APIコールをプロミスでラップしています。これはスコープに追加することができ、Angularのテンプレートシステムによって理解されます。 2つ目はより強引な方法で、コールバックの結果を直接スコープに設定し、そのスコープで
$scope.$digest()
を呼び出して、Angularに外部ソースからの変更を認識させます。
2つの例は直接比較できません。なぜなら最初の例にはログインのステップがないからです。 しかし、一般的にはこのような外部APIとのやり取りは別のサービスにカプセル化し、その結果をプロミスとしてコントローラに渡すことが望ましいとされています。 そうすることで、コントローラを外部環境から切り離し、モックサービスを使用してより簡単にテストすることができます。
関連
-
[解決済み] 'ApplicationSignInManager' が見つからない(ASP.NET MVC)
-
[解決済み] AngularJSのリソースプロミス
-
[解決済み] controllerAs "プロパティを使用する理由は何ですか?
-
[解決済み] AngularJS: ngRouteが動作しない。
-
[解決済み] AngularJSでデータバインディングはどのように機能するのですか?
-
[解決済み] AngularJSを使用して、ブラウザのコンソールで$scope変数にアクセスするにはどうすればよいですか?
-
[解決済み] AngularJSで$scope.$watchと$scope.$applyを使用するにはどうすればよいですか?
-
[解決済み] AngularJS コントローラにおける 'this' と $scope の比較
-
[解決済み] 既存のコールバックAPIをプロミスに変換するにはどうすればよいですか?
-
[解決済み】PromiseとObservablesの違いは何ですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] 適用がすでに進行中のエラー
-
[解決済み] AngularJSのグローバル変数
-
[解決済み] Angular UI-Routerのマルチビュー
-
[解決済み] 'ApplicationSignInManager' が見つからない(ASP.NET MVC)
-
[解決済み] 誰かangularjsの$qサービスの使用について説明してください。[重複しています]。
-
[解決済み] 新しい/分離されたスコープを求める複数のディレクティブ [ngController, ...] がある。
-
[解決済み] angular.serviceとangular.factoryの比較
-
[解決済み] AngularJSのng-repeatでキーと値を反復処理する方法は?
-
[解決済み] コントローラでフィルタを使用するには?
-
[解決済み] AngularJSのサービスをコンソールからテストするにはどうしたらいいですか?