web-dev-qa-db-ja.com

送信されたリストの各アイテムのRxJava遅延

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]

私は何を間違えていますか?

53
athor

そのための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();
57
iagreen

これを行う最も簡単な方法は、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
48
Magnus

間隔を空けてコレクション内の各アイテムを出力する単純なアプローチを共有するだけです。

Observable.just(1,2,3,4,5)
    .zipWith(Observable.interval(500, TimeUnit.MILLISECONDS), (item, interval) -> item)
    .subscribe(System.out::println);

各アイテムは500ミリ秒ごとに放出されます

33
Mina Samy

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)
7
Tim Castelijns

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();
2

まさにあなたが必要とするものだと思います。見て取ります:

long startTime = System.currentTimeMillis();
Observable.intervalRange(1, 5, 0, 50, TimeUnit.MILLISECONDS)
                .timestamp(TimeUnit.MILLISECONDS)
                .subscribe(emitTime -> {
                    System.out.println(emitTime.time() - startTime);
                });
2
Vlad

放出される各アイテム間に遅延を導入することは有用です:

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);

https://github.com/ReactiveX/RxJava/issues/3505 のより良いオプション

1
yaircarreno

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(); 
0
Sanket Kachhela

それほどきれいではない方法は、.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();
0
Tushar Nallan

使用できます

   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)を複製しません。 "私は確信しています"

0
Duy Phan

各グループを遅らせるには、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();
0
kjones