少し前の出来事なので忘れかけています.完全に忘れる前にブログに残しておきます.

Problem

あるとき,Android アプリの開発でこんなコードを書いていました.

  • Itemという Model がある.
  • Itemは ID 文字列idをプロパティとしてもっている.
  • Itemのランキングデータを取得する Web API が用意されていてItemidの一覧が取得出来る.
  • 但し,取得出来るのはidのみなので View に表示するデータ(Item の詳細)は別の API で取得する必要がある.

flatMap()

最初,私はObservable#flatMap()を使って以下のようにしました.

requestRankingItems()
    .flatMap(itemId -> requestItem(itemId))
    .subscribe(item -> { /* bind item to view */ })

しかしこのコードには問題があります.私はランキング情報を View に表示させたかったに,これではランキングの順番がバラバラになってしまいます. flatMapというオペレータは, 順番を保存しないからです. 上の例ではrequestItem(itemId)でサーバにリクエストして,レスポンスが返ってきた順番に値が emit されます. こちらの記事がとても参考になります.

concatMap()

Observable#concatMap()Observable#flatMap()と違って順番を保存してくれます. そこで,コードをこのように変更してみました.

requestRankingItems()
    .concatMap(itemId -> requestItem(itemId))
    .subscribe(item -> { /* bind item to view */ })

ところが,ここで別の問題があります.concatMap()は,ソース Observable の値を 1 つずつ処理します. 現在の値の処理が完了するまで次の処理を行わないのです.つまり,上の例の場合,requestItem(itemId)でサーバにリクエストは,レスポンスが返ってくるまで次のリクエストを行わないのです. これでは,1 リクエスト 1 秒掛かるとすると,20 件の Item を取得するのに 20 秒も待たなければなりません.

リクエストは並列で実行して,結果は順番通りに取得したいんですよ. Javascript のPromise.all()のように. StackOverflowでも質問をしてみましたが期待した答えはもらえませんでした.

concatMapEager()

RxJava の Observable にconcatMapEagerというオペレーターがあります. ドキュメントにはこう記されています.

Maps a sequence of values into Observables and concatenates these Observables eagerly into a single Observable.

どうやら eagerly に concat してくれるらしいです. 更に

Eager concatenation means that once a subscriber subscribes, this operator subscribes to all of the source Observables. The operator buffers the values emitted by these Observables and then drains them in order, each one after the previous one completes.

concatMapEager ではソース Observable(上の例では itemId の Observable)をすべて subscribe してくれます. つまり,すべての id に対して一気にリクエストしてくれます.しかも結果はバッファリングされて,順番通りに emit されます.

requestRankingItems()
    .concatMapEager(itemId -> requestItem(itemId))
    .subscribe(item -> { /* bind item to view */ })

これで OK です.ただしconcatMapEagerは Experimental ですので今後も注目です.

余談

「ブログに書く」という行為をすっかり忘れて暮らしていました.アウトプットはきちんと習慣化したいものですね.

Reference