始めに
2つのストリームを合成する際に、CombineLatest
とWithLatestFrom
を混同していたので一度自分なりに調べてみようと思います。
CombineLatest
こちらのオペレータの特徴はこのようになっています。
- 複数の
IObservable
をまとめられる - すべての
IObservable
から1回以上、値が流れたら後続に流し始める - 値を流し始めたあと、どれか1つの
IObservable
から値を受け取ったらそのIObservable
からの値だけを更新して後続に流す。
1. 複数のIObservableをまとめられる
引数に複数のIObservable
を渡せますし、IEnumerable<IObservable<T>>
の後続に引数なしでCombineLatest
をつけることですべてのIObservable<T>
をまとめることができます。
また、7つまでの合成であれば合成するためのラムダ式を記述して無名関数として渡すことができます。
UniRx/Observable.Concatenate.cs at 284d5c50d3f1ddd9fa7df3d382ea904732a9c2ff · neuecc/UniRx · GitHub より引用
public static IObservable<TResult> CombineLatest<TLeft, TRight, TResult>(this IObservable<TLeft> left, IObservable<TRight> right, Func<TLeft, TRight, TResult> selector) { return new CombineLatestObservable<TLeft, TRight, TResult>(left, right, selector); } public static IObservable<IList<T>> CombineLatest<T>(this IEnumerable<IObservable<T>> sources) { return CombineLatest(sources.ToArray()); } public static IObservable<IList<TSource>> CombineLatest<TSource>(params IObservable<TSource>[] sources) { return new CombineLatestObservable<TSource>(sources); } public static IObservable<TR> CombineLatest<T1, T2, T3, TR>(this IObservable<T1> source1, IObservable<T2> source2, IObservable<T3> source3, CombineLatestFunc<T1, T2, T3, TR> resultSelector) { return new CombineLatestObservable<T1, T2, T3, TR>(source1, source2, source3, resultSelector); } . . . . . . . . . .
2. すべてのIObservableから1回以上、値が流れたら後続に流し始める
下の図のように合成しているIObservable
のすべてから1回以上値が流れないとCombineLatest
から値が流れ始めません。
3. 値を流し始めたあと、どれか1つのIObservableから値を受け取ったらそのIObservableからの値だけを更新して後続に流す。
値を流し始めたあとはどれか1つから値が流れたらその値だけを更新し、それ以外からの値は最新のものを使って後続に値を流します。
WithLatestFrom
こちらのオペレータの特徴は次のようになっています。
- 2つの
IObservable
(メイン、サブ)をまとめられる - サブの
IObservable
から1回以上値が流されている状態でメインから流れた時に値を流し始める - 値を流し始めたあと、メインの
IObservable
から値が渡されたときのみ後続に値を流す。
1. 2つのObservable(メイン、サブ)をまとめられる
WithLatestFrom
ではメインとなるIObservable
とサブとなるIObservable
の2つをまとめられます。 それ以上のObservable
をまとめることはできません。
UniRx/Observable.Concatenate.cs at 284d5c50d3f1ddd9fa7df3d382ea904732a9c2ff · neuecc/UniRx · GitHub より引用
public static IObservable<TResult> WithLatestFrom<TLeft, TRight, TResult>(this IObservable<TLeft> left, IObservable<TRight> right, Func<TLeft, TRight, TResult> selector) { return new WithLatestFromObservable<TLeft, TRight, TResult>(left, right, selector); }
2. サブのObservableから1つ以上値が流されている状態でメインから流れた時に値を流し始める
サブから1つ以上の値が流れた状態でないと、メインからいくら値が流されても値が流れません。
3. 値を流し始めたあと、メインのObservableから値が渡されたときのみ後続に値を流す。
値が流れている状態ではメインから値が流れたときのみ、後続に値を流します。 値を流すときは最新のサブからの値とメインからの値を合成します。
2つの比較
CombineLatest | WithLatstFrom | |
---|---|---|
合成できる個数 | 2つ以上 | 2つ(メインとサブのIObservable )のみ |
値を流し始める基準 | すべてのIObservable から1回以上値が流れる |
サブから1回以上値が流れている状態でメインから値が流れる |
値を流す基準 | どれか一つのIObservable から値が流れる |
メインから値が流れる |
使い分け
言わずもがな2つより多いIObservable
を合成する場合はCombineLatest
を使うことになるでしょう。
2つのObservable
を合成するときでも、両方のIObservable
で発火したい場合はCombineLatest
、1つのIObservable
からのみ発火したい場合はWithLatestFrom
になるでしょう。
2つのIObservable
から1回だけ値が流れる場合は、順番関係なく2つから流れたときに後続に流し始めるCombineLatest
の方が良いでしょう。もし、順番が明確であればWithLatestFrom
でも問題ないかもしれません。
ただ、使い分けが難しいと感じたのが順番が明確ではなく、1つのIObservable
のみ発火してほしい場合だと感じています。その場合は、どうにかして決まった順で流れるようにするかもう一方から値が流れて発火した場合を許容するかになるかもしれません。
参考
Rxのマーブルを自由に移動させて、オペレータに通すとどうなるか動的にわかるサイトになっているので参考になります。
また、いくつかの画像作成で使用しました。