web-dev-qa-db-fra.com

Comment «attendre» deux observables dans RxJS

Dans mon application, j'ai quelque chose comme:

this._personService.getName(id)
      .concat(this._documentService.getDocument())
      .subscribe((response) => {
                  console.log(response)
                  this.showForm()
       });

 //Output: 
 // [getnameResult]
 // [getDocumentResult]

 // I want:
 // [getnameResult][getDocumentResult]

Ensuite, j'obtiens deux résultats séparés, d'abord du _personService, Puis du _documentService. Comment puis-je attendre les deux résultats avant d'appeler this.showForm() pour terminer puis manipuler les résultats de chacun?.

45
gog

Mise à jour: octobre 2018

J'ai déjà suggéré l'utilisation de la méthode Zip. Cependant, depuis lors, je me suis retrouvé à utiliser combineLatest à la place. Alors décidé d'ajouter combineLatest.

CombineLatest émet les dernières valeurs émises par les observables. Alors que Zip la méthode émet les éléments émis dans l'ordre sequence.

Par exemple, si l’observable n ° 1 émet son élément rd et que l’observable n ° 2 a émis son élément 5th. Le résultat en utilisant la méthode Zip sera les rd valeurs émises des deux observables.

Dans cette situation, le résultat en utilisant combineLatest sera le 5th et rd. qui se sent plus naturel.


combineLatest (observables)

De reactiveX documentation :

Chaque fois qu'une entrée Observable émet une valeur, elle calcule une formule en utilisant les dernières valeurs de toutes les entrées, puis émet la sortie de cette formule.

// Observables to combine
const name$ = this._personService.getName(id);
const document$ = this._documentService.getDocument();

combineLatest(name$, document$, (name, document) => ({name, document}))
    .subscribe(pair => {
           this.name = pair.name;
           this.document = pair.document;
           this.showForm();
       })

Observable.Zip (observables)

La méthode Observable.Zip est expliquée dans documentation reactiveX :

Combine plusieurs observables pour créer un observable dont les valeurs sont calculées à partir des valeurs, dans l'ordre, de chacun des observables en entrée.

// Observables to combine
const name$ = this._personService.getName(id);
const document$ = this._documentService.getDocument();

Observable
    .Zip(name$, document$, (name: string, document: string) => ({name, document}))
    .subscribe(pair => {
           this.name = pair.name;
           this.document = pair.document;
           this.showForm();
       })

une note de côté (s'applique aux deux méthodes)

Le dernier paramètre, où nous avons fourni une fonction, (name: string, document: string) => ({name, document}) est facultatif. Vous pouvez le sauter ou effectuer des opérations plus complexes:

Si le dernier paramètre est une fonction, cette fonction est utilisée pour calculer la valeur créée à partir des valeurs d'entrée. Sinon, un tableau des valeurs en entrée est renvoyé.

Donc, si vous sautez la dernière partie, vous obtenez un tableau:

// Observables to combine
const name$ = this._personService.getName(id);
const document$ = this._documentService.getDocument();

Observable
    .Zip(name$, document$)
    .subscribe(pair => {
           this.name = pair['0'];
           this.document = pair['1'];
           this.showForm();
       })
57
Hamid Asghari

Utilisez la méthode forkJoin() des observables. Voir ce lien pour référence

Depuis le RXJS docs

Cet opérateur est mieux utilisé lorsque vous avez un groupe d'observables et que vous ne vous souciez que de la valeur finale émise de chacun. Un cas d'utilisation courant pour cela est si vous souhaitez émettre plusieurs demandes lors du chargement de la page (ou un autre événement) et que vous souhaitez agir uniquement lorsqu'une réponse a été reçue pour tous. De cette façon, cela ressemble à la façon dont vous pourriez utiliser Promise.all

Observable.forkJoin([character, characterHomeworld]).subscribe(results => {
  // results[0] is our character
  // results[1] is our character homeworld
  results[0].homeworld = results[1];
  this.loadedCharacter = results[0];
});

Code extrait de: https://coryrylan.com/blog/angular-multiple-http-requests-with-rxjs

23
Prathmesh Dali

Les opérateurs RxJS pour les nuls: forkJoin, Zip, combineLatest, withLatestFrom m'ont beaucoup aidé. Comme son nom l'indique, il décrit les opérateurs de combinaison suivants:

N'importe lequel d'entre eux pourrait être ce que vous recherchez, cela dépend du cas. Consultez l'article pour plus d'informations.

5
nyxz

Pour moi, ce échantillon était la meilleure solution.

const source = Observable.interval(500);
const example = source.sample(Observable.interval(2000));
const subscribe = example.subscribe(val => console.log('sample', val));

Donc .. seulement lorsque second (exemple) émet - vous verrez la dernière valeur émise de premier (source).

Dans ma tâche, j'attends la validation du formulaire et tout autre événement DOM.

2

Regardez la méthode 'combineLatest', cela pourrait être approprié ici. http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#static-method-combineLatest

const { Observable } = Rx

const name$ = this._personService.getName(id);
const document$ = this._documentService.getDocument();

Observable
    .combineLatest(name$, document$, (name, document) => ({ name, document }))
    .first() // or not, implementation detail
    .subscribe(({ name, document }) => {
        // here we have both name and document
        this.showForm()
    })
1
Tal

Vous pouvez utiliser "Zip" ou "tampon" comme suit.

function getName() {
    return Observable.of('some name').delay(100);
}

function getDocument() {
    return Observable.of('some document').delay(200);
}

// CASE1 : concurrent requests
Observable.Zip(getName(), getDocument(), (name, document) => {
    return `${name}-${document}`;
})
    .subscribe(value => console.log(`concurrent: ${value}`));

// CASE2 : sequential requests
getName().concat(getDocument())
    .bufferCount(2)
    .map(values => `${values[0]}-${values[1]}`)
    .subscribe(value => console.log(`sequential: ${value}`));
0
thatseeyou