web-dev-qa-db-fra.com

Séparez les valeurs observables par quantité de temps spécifique dans RxJS

Quelle serait la façon la plus idiomatique de produire des valeurs d'un observable dans un laps de temps spécifique? Par exemple, disons que j'ai un observable créé à partir d'un grand tableau et que je veux donner une valeur toutes les 2 secondes. Une combinaison de interval et selectMany est-elle la meilleure façon?

29
Sergi Mansilla

Pour votre exemple spécifique, l'idée est de mapper chaque valeur du tableau à un observable qui donnera son résultat après un délai, puis de concaténer le flux résultant d'observables:

var delayedStream = Rx.Observable
    .fromArray([1, 2, 3, 4, 5])
    .map(function (value) { return Rx.Observable.return(value).delay(2000); })
    .concatAll();

D'autres exemples pourraient en effet utiliser timer ou interval. Ça dépend.

Par exemple, si votre tableau est vraiment très volumineux, alors ce qui précède entraînera une quantité importante de pression sur la mémoire (car il crée N observables pour un très grand N). Voici une alternative qui utilise interval pour parcourir le tableau paresseusement:

var delayedStream = Rx.Observable
    .interval(2000)
    .take(reallyBigArray.length) // end the observable after it pulses N times
    .map(function (i) { return reallyBigArray[i]; });

Celui-ci donnera la valeur suivante du tableau toutes les 2 secondes jusqu'à ce qu'il ait itéré sur tout le tableau.

31
Brandon

Je pense que l'utilisation de Zip produit un code meilleur et plus lisible, en utilisant toujours seulement 3 observables.

var items = ['A', 'B', 'C'];

Rx.Observable.Zip(
  Rx.Observable.fromArray(items),
  Rx.Observable.timer(2000, 2000),  
  function(item, i) { return item;}
)
24
farincz

Alors que la réponse de Brandon obtient l'essentiel de l'idée, voici une version qui donne immédiatement le premier élément, puis met du temps entre les éléments suivants.

var delay = Rx.Observable.empty().delay(2000);

var items = Rx.Observable.fromArray([1,2,3,4,5])
  .map(function (x) {
    return Rx.Observable.return(x).concat(delay); // put some time after the item
  })
  .concatAll();

Mis à jour pour les nouveaux RxJS:

var delay = Rx.Observable.empty().delay(2000);

var items = Rx.Observable.fromArray([1,2,3,4,5])
  .concatMap(function (x) {
    return Rx.Observable.of(x).concat(delay); // put some time after the item
  });
8
cwharris

Pour RxJS 5:

Rx.Observable.from([1, 2, 3, 4, 5])
  .Zip(Rx.Observable.timer(0, 2000), x => x)
  .subscribe(x => console.log(x));
5
user3587412

Comme cela n'a pas été mentionné, je pense que concatMap combiné avec delay est assez lisible.

Rx.Observable.fromArray([1, 2, 3, 4, 5])
    .concatMap(x => Rx.Observable.of(x).delay(1000));

Voir https://codepen.io/jmendes/pen/EwaPzw

4
Juan Mendes

Convenez que Zip est une approche propre. Voici une fonction réutilisable pour générer un flux d'intervalle pour un tableau:

function yieldByInterval(items, time) {
  return Rx.Observable.from(items).Zip(
    Rx.Observable.interval(time),
    function(item, index) { return item; }
  );
}

// test
yieldByInterval(['A', 'B', 'C'], 2000)
  .subscribe(console.log.bind(console));

Cela s'appuie sur réponse de farincz , mais est légèrement plus court en utilisant .Zip Comme méthode d'instance.

En outre, j'ai utilisé Rx.Observable.from() parce que Rx.Observable.fromArray() est obsolète .

4
Robert Penner

Pour RxJS v6, obtenir le suivant avec un retard de 2 secondes.

Exemple 1. concatMap:

import {of} from 'rxjs';
import {concatMap, delay} from 'rxjs/operators';

of(1, 2, 3, 4, 5)
  .pipe(
    concatMap(x => of(x)
      .pipe(
        delay(2000))
    )
  )
  .subscribe({
    next(value) {
      console.log(value);
    }
  });

Exemple 2. map + concatAll:

import {of} from 'rxjs';
import {concatAll, delay, map} from 'rxjs/operators';

of(1, 2, 3, 4, 5)
  .pipe(
    map(x => of(x)
      .pipe(
        delay(2000))
    ),
    concatAll()
  )
  .subscribe({
    next(value) {
      console.log(value);
    }
  });
1
ZoomAll

En s'appuyant sur les solutions Zip de farincz et user3587412, voici comment cela fonctionne dans RxJS v6

const { Zip, from, timer } = require("rxjs")
const { map } = require("rxjs/operators")

const input = [1, 2, 3, 4, 5]
const delay = 2000

Zip(
    from(input),
    timer(0, delay)
).pipe(
    map(([ delayedInput, _timer ]) => delayedInput) // throw away timer index
).subscribe(
    console.log
)
0
Anonymous