Rxではかなりシンプルだと思っていたものを実装するのに苦労しています。
アイテムのリストがあり、各アイテムを遅延して発行したいです。
Rx delay()演算子は、個々のアイテムではなく、指定された遅延だけすべてのアイテムの放出をシフトするようです。
以下にテストコードを示します。リスト内のアイテムをグループ化します。その後、各グループには、送信される前に遅延が適用されます。
Observable.range(1, 5)
.groupBy(n -> n % 5)
.flatMap(g -> g.toList())
.delay(50, TimeUnit.MILLISECONDS)
.doOnNext(item -> {
System.out.println(System.currentTimeMillis() - timeNow);
System.out.println(item);
System.out.println(" ");
}).toList().toBlocking().first();
結果は次のとおりです。
154ms
[5]
155ms
[2]
155ms
[1]
155ms
[3]
155ms
[4]
しかし、私が期待するのは次のようなものです。
174ms
[5]
230ms
[2]
285ms
[1]
345ms
[3]
399ms
[4]
私は何を間違えていますか?
そのための1つの方法は、 Zip
を使用して、オブザーバブルを Interval
オブザーバブルと組み合わせて出力を遅延させることです。
Observable.Zip(Observable.range(1, 5)
.groupBy(n -> n % 5)
.flatMap(g -> g.toList()),
Observable.interval(50, TimeUnit.MILLISECONDS),
(obs, timer) -> obs)
.doOnNext(item -> {
System.out.println(System.currentTimeMillis() - timeNow);
System.out.println(item);
System.out.println(" ");
}).toList().toBlocking().first();
これを行う最も簡単な方法は、concatMapを使用し、各アイテムを遅延Obserableでラップするだけのようです。
long startTime = System.currentTimeMillis();
Observable.range(1, 5)
.concatMap(i-> Observable.just(i).delay(50, TimeUnit.MILLISECONDS))
.doOnNext(i-> System.out.println(
"Item: " + i + ", Time: " + (System.currentTimeMillis() - startTime) +"ms"))
.toCompletable().await();
プリント:
Item: 1, Time: 51ms
Item: 2, Time: 101ms
Item: 3, Time: 151ms
Item: 4, Time: 202ms
Item: 5, Time: 252ms
間隔を空けてコレクション内の各アイテムを出力する単純なアプローチを共有するだけです。
Observable.just(1,2,3,4,5)
.zipWith(Observable.interval(500, TimeUnit.MILLISECONDS), (item, interval) -> item)
.subscribe(System.out::println);
各アイテムは500ミリ秒ごとに放出されます
Kotlinユーザー向けに、「Zip with interval」アプローチの拡張関数を作成しました
import io.reactivex.Observable
import io.reactivex.functions.BiFunction
import Java.util.concurrent.TimeUnit
fun <T> Observable<T>.delayEach(interval: Long, timeUnit: TimeUnit): Observable<T> =
Observable.Zip(
this,
Observable.interval(interval, timeUnit),
BiFunction { item, _ -> item }
)
同じように機能しますが、これにより再利用可能になります。例:
Observable.range(1, 5)
.delayEach(1, TimeUnit.SECONDS)
MinRegularIntervalDelayOperator
などの カスタムrx演算子 を実装してから、lift
関数で使用できます。
Observable.range(1, 5)
.groupBy(n -> n % 5)
.flatMap(g -> g.toList())
.lift(new MinRegularIntervalDelayOperator<Integer>(50L))
.doOnNext(item -> {
System.out.println(System.currentTimeMillis() - timeNow);
System.out.println(item);
System.out.println(" ");
}).toList().toBlocking().first();
まさにあなたが必要とするものだと思います。見て取ります:
long startTime = System.currentTimeMillis();
Observable.intervalRange(1, 5, 0, 50, TimeUnit.MILLISECONDS)
.timestamp(TimeUnit.MILLISECONDS)
.subscribe(emitTime -> {
System.out.println(emitTime.time() - startTime);
});
放出される各アイテム間に遅延を導入することは有用です:
List<String> letters = new ArrayList<>(Arrays.asList("a", "b", "c", "d"));
Observable.fromIterable(letters)
.concatMap(item -> Observable.interval(1, TimeUnit.SECONDS)
.take(1)
.map(second -> item))
.subscribe(System.out::println);
ConcatMapはソースアイテムのobservableを返すので、concatMapを使用してそれを行う他の方法があります。そのため、そのオブザーバブルに遅延を追加できます。
ここで私が試したもの。
Observable.range(1, 5)
.groupBy(n -> n % 5)
.concatMap(integerIntegerGroupedObservable ->
integerIntegerGroupedObservable.delay(2000, TimeUnit.MILLISECONDS))
.doOnNext(item -> {
System.out.println(System.currentTimeMillis() - timeNow);
System.out.println(item);
System.out.println(" ");
}).toList().toBlocking().first();
それほどきれいではない方法は、.delay(Func1)演算子を使用して、反復で遅延を変更することです。
Observable.range(1, 5)
.delay(n -> n*50)
.groupBy(n -> n % 5)
.flatMap(g -> g.toList())
.doOnNext(item -> {
System.out.println(System.currentTimeMillis() - timeNow);
System.out.println(item);
System.out.println(" ");
}).toList().toBlocking().first();
使用できます
Observable.interval(1, TimeUnit.SECONDS)
.map(new Function<Long, Integer>() {
@Override
public Integer apply(Long aLong) throws Exception {
return aLong.intValue() + 1;
}
})
.startWith(0)
.take(listInput.size())
.subscribe(new Consumer<Integer>() {
@Override
public void accept(Integer index) throws Exception {
Log.d(TAG, "---index of your list --" + index);
}
});
上記のコードはvalue(index)を複製しません。 "私は確信しています"
各グループを遅らせるには、flatMap()
を変更して、グループの放出を遅らせるObservableを返すことができます。
Observable
.range(1, 5)
.groupBy(n -> n % 5)
.flatMap(g ->
Observable
.timer(50, TimeUnit.MILLISECONDS)
.flatMap(t -> g.toList())
)
.doOnNext(item -> {
System.out.println(System.currentTimeMillis() - timeNow);
System.out.println(item);
System.out.println(" ");
}).toList().toBlocking().first();