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?
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();
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
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.
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();
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)
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();
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
Observable.just("A", "B", "C", "D", "E", "F")
.flatMap { item -> Thread.sleep(2000)
Observable.just( item ) }
.subscribe { println( it ) }
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);
});
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();
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"
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();