AngularJSでは非同期処理のAPIとしてPromise API($q Service)が用意されています。
このPromise APIですがAngularJS v1.2でPromise/A+に準拠した形になりました。
Promise/A+の詳細については下記を参照してください。
AngularJSのPromise APIは非同期処理としては最小限の機能しか用意されておらず、複雑な非同期処理を書く場合は何かしらのライブラリが利用したくなります。
非同期処理のライブラリとしてはJSDeferredやjQuery.Deferredなどが有名ですが、今回はRxJSを使ってみます。
RxJSもv2.2でPromise/A+に準拠したObservable.fromPromiseが追加されたので、AngularJSのPromiseとRxJSを連携させて使うことが簡単にできるようになりました。
というわけで、AngularJSで用意されている$resource (Web APIを呼び出すためのサービス) を例にして、RxJSと連携させる方法を紹介したいと思います。
それぞれの基本的な使い方は省略するので、各種ドキュメントを参考にしてください。
単純な処理の比較
まずAngularJSのPromiseを使った場合と、RxJSを使った場合の単純な処理の書き方を比較してみます。
リクエストを投げて成功した場合と失敗した場合のコールバックをセットするには以下のように書きます。
これだと、Promiseを使ったほうが簡単ですね。
var res = $resource("/hoge");
res.get().$promise
.then(
function(data){ alert("success: " + data); },
function(err){ alert("failure: " + err); }
);
var res = $resource("/hoge");
Rx.Observable.fromPromise(res.get().$promise)
.subscribe(
function(data){ alert("success: " + data); },
function(err){ alert("failure: " + err); }
);
1つ目のリクエストが完了したら、その結果を使って次のリクエストを呼び出す場合は以下のように。
これも、Promiseのほうがthenをつなげて書くだけなので分かりやすいですね・・・
var res1 = $resource("/hoge");
var res2 = $resource("/fuga/:id");
res1.get().$promise
.then(function(data){
return res2.get({id: data.id}).$promise;
})
.then(
function(data){ alert("success: " + data); },
function(err){ alert("failure: " + err); }
);
var res1 = $resource("/hoge");
var res2 = $resource("/fuga/:id");
Rx.Observable.fromPromise(res1.get().$promise)
.selectMany(function(data){
return Rx.Observable.fromPromise(res2.get({id: data.id}).$promise);
})
.subscribe(
function(data){ alert("success: " + data); },
function(err){ alert("failure: " + err); }
);
RxJSの便利なメソッド
ここからは、Promiseだけで実装しようとすると複雑になるような処理を、RxJSで書いてみたいと思います。
まず、リクエストに成功するまで最大3回リトライする処理はこんな感じで書けます。
var res = $resource("/hoge")
Rx.Observable.defer(function(){
return Rx.Observable.fromPromise(res.get().$promise);
})
.retry(3)
.subscribe(
function(data){ alert("success: " + data); },
function(err){ alert("failure: " + err); }
);
複数のリクエストを投げて、その結果が揃うまで待ってから次の処理をするというのも簡単に書けます。
var res1 = $resource("/hoge");
var res2 = $resource("/fuga");
Rx.Observable.fromPromise(res1.get().$promise)
.zip(Rx.Observable.fromPromise(res2.get().$promise),
function(data1, data2){ return [data1, data2]; }
)
.subscribe(
function(data){ alert("success: " + data[0] + ":" + data[1]); },
function(err){ alert("failure: " + err); }
);
今回は簡単な例ばかりを示しましたが、RxJSには非常に便利なAPIがたくさん用意されているのでぜひ使ってみてください。