AngularでRxJSが使われる理由がわからず色々調べたら腹落ちした
はじめに
ここ半年ほど、とあるWebサービスの管理機能をAngular6/7系で書いてます。
AngularでサーバーサイドのWebAPIと連携する処理を書く時に以下のようなロジックになるかと思います。
import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; export class SomeService { find(id: number): Observable<any> { const url = `/xxxxx/${id}`; return this.http .get<any>(url) .pipe(map(response => response.data.product)); }
上記のSomeServiceクラスの中で行われてる処理は以下のようなことになるのですが、個人的に当初疑問に思ったことがあったので以下で詳しくまとめていきます
- findメソッドの戻り値は Observable objectを返す
- Observable objectに対する操作は色々ある
- 上記のコードの場合にはpipe()メソッドでmap()オペレータを適用している。
- 詳しいことは以下の引用文を参考
RxJS 6では、オペレータはpipe()メソッドの引数に渡さなければなりません。このメソッドそのものは、バージョン5.5で備わったものです(「Operator pipe syntax」)。けれどこのときは、Observableのメソッドとしてオペレータを呼び出すバージョン5の書き方もできました。それがpipe()メソッド一択になったということです。 RxJS 6: オペレータをつくってみる
そもそもRxJSとは何?
関数型プログラミングの文脈で出てきそうなmapなどの名前が出てきてるので、やってる処理は何となくイメージは付いていたのですが、そもそもRxJSって何だろうというベースがわかってませんでした。
そんな理解の中で
- serviceクラスの中で必ずと言って良いほど出てくるのでサーバーサイドとの通信処理など、非同期処理の結果を適切に処理するためにRxJSが利用されてるっぽいなぁ
- AngularJSの時代なら$http や$resourceなどを利用するコードだったけど、その時はRxJS相当のロジックはなかったけど何故こんなことをするんだろうか 🤔
- 単に非同期処理をスッキリ書くだけなら別のアプローチもありそう
という感じで、他の人が書いた実装を読んでいたのですが、ふと
「最後の非同期処理をスッキリ書くだけならPromise使った書き方でも良いのかな?」
と思ったのでそこを少し掘り下げてみました
非同期処理するだけなら、ES6のPromise使った処理でも良いのでは?
let getURL = (URL) => { return new Promise((resolve, reject) => { let req = new XMLHttpRequest(); req.open('GET', URL, true); req.onload = () => { if (req.status === 200) { let result = JSON.parse(req.responseText); resolve(result); } else { reject(new Error(req.statusText)); } }; req.send(); }); };
【ES6】Promiseクラスの基本的な使い方より上記コードは引用してますが、ざっくりとやってることを書くと
- Promiseをインスタンス化
- インスタンス化のときに成功時(resolve)、失敗時(reject)の2つの引数を渡す
- XMLHttpRequest通じて通信処理が実行されて、HTTPのステータスコードによって成功時、失敗時それぞれの処理を呼ばれる
という流れになるかと思います。
ES6/7の時代なので、こういうPromise使った処理でも良いのではと最初思ってました。
とあるQiitaの記事でObservableの位置づけがよくわかった
「Angular Promise Observable 」みたいなキーワードで適当にググってた中で、少し古いのですがAngular2のHttpモジュールを眺めてベストプラクティスを考えるのPromise v.s. Observableという記事に行き当たり、こちらの記事を読んでいて、何故Observableが登場してきたのか時代背景みたいなものを考えた時に、すごく腹落ちできました。
Promise ではなく、Observableを使うポイント
上記のQiitaの記事に書かれてるように、最近のWebアプリケーションで非同期処理が必要な場面はいくつかあると思います。
記事中では6つに大別してましたが、自分の最近の仕事では
- Ajax
- Promiseで対処できる
- ただし Promise使った場合にキャンセルできないためレスポンスを待っている間にページが切り替わっても中断は不可 という欠点がある
- DOM Event
- Animations
という3つがひとまず対象になるのでそこだけで考えたとしても、Promiseが対処できる箇所は少ない上に、Promiseの特徴として
- 連続した非同期処理の結果を扱えない
- 遅延実行できない
- 即実行され、resolveした直後にthenが実行される
という点があるためにAngularではRxJSを利用した処理になってる。
逆にRxJSを利用することで
- 上記のような非同期処理はすべて対処できる
- ストリームという概念を導入してるので、Ajax&DOM Eventなど異なる非同期処理を集約&連続して扱える
などのことが行えるため、色々な種類の非同期処理を同じように扱えるRxJSを使うのがベストという結論になるっていうので、点と点のような状態だった自分の理解がようやく1つにつながりました 😉
まとめ
単にAjaxな処理だけするならPromiseでも良いかもしれないけど、最近のWebアプリケーションではユーザーイベント(例:テキストボックスの入力内容や状態)を検知して、ある状況になったらサーバーサイドにリクエストを投げる・・・といった色々な種類の非同期処理が連携する必要が出てきます。
PromiseはそもそもAjaxな処理しかカバーしてない上に、連続した非同期処理の結果を扱えないことも考えるとAngular2でRxJSを利用する処理が採用されてそれが今に至ったんだなぁーというのがすごく納得できて腹落ちしました✌