web-dev-qa-db-fra.com

Délai RxJava pour chaque élément de la liste émise

J'ai du mal à mettre en œuvre quelque chose qui, à mon avis, serait assez simple dans Rx.

J'ai une liste d'éléments et je souhaite que chaque élément soit émis avec un délai.

Il semble que l’opérateur Rx delay () décale simplement l’émission de tous les éléments du délai spécifié, et non de chaque élément.

Voici un code de test. Il regroupe des éléments dans une liste. Chaque groupe devrait ensuite avoir un délai appliqué avant d'être émis.

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

Le résultat est:

154ms
[5]

155ms
[2]

155ms
[1]

155ms
[3]

155ms
[4]

Mais ce à quoi je m'attendrais, c'est à peu près ceci:

174ms
[5]

230ms
[2]

285ms
[1]

345ms
[3]

399ms
[4]

Qu'est-ce que je fais mal?

46
athor

Une façon de le faire est d'utiliser Zip pour associer votre observable à un Interval pour retarder la sortie. 

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();
49
iagreen

Le moyen le plus simple de le faire semble simplement utiliser concatMap et envelopper chaque élément dans un délai d'attente. 

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

Impressions: 

Item: 1, Time: 51ms
Item: 2, Time: 101ms
Item: 3, Time: 151ms
Item: 4, Time: 202ms
Item: 5, Time: 252ms
40
Magnus

Il suffit de partager une approche simple pour émettre chaque élément d'une collection avec un intervalle:

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

Chaque élément sera émis toutes les 500 millisecondes.

28
Mina Samy

Vous pouvez implémenter un opérateur personnalisé tel que MinRegularIntervalDelayOperator , puis l'utiliser avec la fonction 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
Matias Irland Tomas

Pour les utilisateurs de kotlin, j'ai écrit une fonction d'extension pour l'approche 'Zip avec intervalle' 

fun <T> Observable<T>.delayEach(interval: Long, timeUnit: TimeUnit): Observable<T> =
        Observable.Zip(this, Observable.interval(interval, timeUnit), { item, _ -> item })

Cela fonctionne de la même manière, mais cela le rend réutilisable. Exemple:

Observable.range(1, 5)
    .delayEach(1, TimeUnit.SECONDS)
2
Tim Castelijns

Il existe un autre moyen de le faire en utilisant concatMap car concatMap renvoie les éléments source observables. afin que nous puissions ajouter un délai sur cette observable.

voici ce que j'ai essayé.

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

Il est utile d'introduire un délai entre chaque élément émis:

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

Plus de bonnes options sur https://github.com/ReactiveX/RxJava/issues/3505

0
yaircarreno
Observable.just("A", "B", "C", "D", "E", "F")
    .flatMap { item -> Thread.sleep(2000)
        Observable.just( item ) }
    .subscribe { println( it ) }
0
Vairavan

Je pense que c'est exactement ce dont vous avez besoin. Regardez:

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

Une méthode moins propre consiste à modifier le délai avec l'itération à l'aide de l'opérateur.

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

Vous pouvez utiliser 

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

Ce code ci-dessus ne duplique pas la valeur (index). "Je suis sûr"

0
Duy Phan

Pour retarder chaque groupe, vous pouvez changer votre flatMap() pour renvoyer un observable retardant l’émission du groupe.

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