Wie man in RxJS auf zwei Observablen wartet

75

In meiner App habe ich so etwas wie:

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

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

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

Dann bekomme ich zwei getrennte Ergebnisse, zuerst das _personServiceund dann das _documentService. Wie kann ich auf beide Ergebnisse warten, bevor der Aufruf abgeschlossen ist this.showForm(), und dann die Ergebnisse der einzelnen bearbeiten?

gog
quelle
für das, was ich verstehe, sind Sie bereits kraftconcat
user3743222
@ user3743222 In diesem Fall werden die Werte separat ausgegeben. Einer nach dem anderen.
Gog
1
Beim Abonnieren von forkJoin erhalten Sie ein Ergebnis - Tupel mit der ersten und zweiten Antwort - genau das haben Sie gefragt?
Julia Passynkova
Sie können sich forkJoin in meinem Blog-Beitrag ansehen
Julia Passynkova

Antworten:

102

Letzte Aktualisierung: Mai 2020.

combinLatest (Observablen)

Aus der reactiveX- Dokumentation :

Immer wenn eine Eingabe Observable einen Wert ausgibt, berechnet sie eine Formel unter Verwendung der neuesten Werte aus allen Eingaben und gibt dann die Ausgabe dieser Formel aus.

(Update: Mai 2020) Während das andere Beispiel gültig bleibt, gibt es hier eine neue Syntax:

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

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

combinLatest (Observables) (alternative Syntax):

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

zip vs combinLatest

(Update: Okt. 2018) Ich habe zuvor die Verwendung derzipMethode vorgeschlagen. Für einige AnwendungsfällecombineLatesthat dies jedoch einige Vorteile gegenüberzip. Daher ist es wichtig, die Unterschiede zu verstehen.

CombineLatestgibt die zuletzt emittierten Werte von Observablen aus. Während die zipMethode die ausgegebenen Elemente in der Reihenfolge ihrer Reihenfolge ausgibt .

Zum Beispiel, wenn Observable # 1 sein drittes Element ausstrahlt und Observable # 2 sein 5. Element ausgibt. Das Ergebnis unter Verwendung der zipMethode ist der 3. emittierte Wert von beiden observables.

In dieser Situation ist das Ergebnis combineLatestdie 5. und 3 .. das fühlt sich natürlicher an.


Observable.zip (Observables)

(Ursprüngliche Antwort: Jul, 2017) Die Observable.zip-Methode wird in der reactiveX-Dokumentation erläutert:

Kombiniert mehrere Observables, um ein Observable zu erstellen, dessen Werte aus den Werten der eingegebenen Observables in der Reihenfolge berechnet werden.

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

eine Randnotiz (gilt für beide Methoden)

Der letzte Parameter, für den wir eine Funktion bereitgestellt haben, (name: string, document: string) => ({name, document})ist optional. Sie können es überspringen oder komplexere Vorgänge ausführen:

Wenn der letzte Parameter eine Funktion ist, wird diese Funktion verwendet, um den erstellten Wert aus den Eingabewerten zu berechnen. Andernfalls wird ein Array der Eingabewerte zurückgegeben.

Wenn Sie also den letzten Teil überspringen, erhalten Sie ein Array:

// 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();
       })
Hamid Asghari
quelle
Ist es möglich, auf die Fertigstellung eines Observablen zu warten? Mein Observable hat ein anderes inneres Observable, das wiederum auf einem http.get basiert?
HDJEMAI
Warum sollten Sie in Ihrem Update vom Januar 2020 das Array einem Objekt zuordnen? Scheint ein unnötiger Schritt zu sein, wenn Sie das Array innerhalb der Subscribe-Methode zerstören könnten. Das wäre mein einziger Kommentar, der Rest sieht gut aus.
Craig Myles
OOOOHHHH, ich habe eine Stunde lang nach dem Operator combLateest gesucht ... vielen Dank
Andrew
Wie können Sie sich von den kombinierten Observablen abmelden? Melden Sie sich einfach von dem Observable ab, das von CombinedLatest () zurückgegeben wird. Subscribe ()?
Sharon182
@ sharon182 genau.
Hamid Asghari
34

Verwenden Sie die forkJoin()Methode der Observablen. Überprüfen Sie diesen Link als Referenz

Aus den RXJS- Dokumenten

Dieser Operator wird am besten verwendet, wenn Sie eine Gruppe von Observablen haben und sich nur um den endgültigen emittierten Wert jedes Operators kümmern. Ein häufiger Anwendungsfall hierfür ist, wenn Sie beim Laden der Seite (oder eines anderen Ereignisses) mehrere Anforderungen stellen und nur dann Maßnahmen ergreifen möchten, wenn eine Antwort für alle eingegangen ist. Auf diese Weise ähnelt es der Verwendung von 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 entnommen aus: https://coryrylan.com/blog/angular-multiple-http-requests-with-rxjs

Prathmesh Dali
quelle
Ist es möglich, auf die Fertigstellung eines Observablen zu warten? Mein Observable hat ein anderes inneres Observable, das wiederum auf einem http.get basiert?
HDJEMAI
@HDJEMAI Wenn du etw tun willst. Können Sie nach Abschluss eines Observable ein Nesting-Abonnement verwenden?
Yuwei HE
16

Die RxJS-Operatoren für Dummies: ForkJoin, Zip, CombineLatest, WithLatestFrom haben mir sehr geholfen. Wie der Name schon sagt, werden die folgenden Kombinationsoperatoren beschrieben:

Jeder von ihnen könnte das sein, wonach Sie suchen, hängt vom Fall ab. Weitere Informationen finden Sie im Artikel.

nyxz
quelle
Vielen Dank, der erste Link, den Sie gegeben haben, dh "RxJs Operators ...", ist ein Muss und die beste und einfachste Erklärung, die Sie jemals bekommen können
Ram
4

Verbesserung der Hamid Asghari-Antwort, die direkte Argumentzerlegung verwendet und automatisch Typen hinzufügt (wenn Sie Typoskript verwenden)

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

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

BONUS : Sie können Fehler auch wie folgt behandeln

Kamil Kiełczewski
quelle
2

Für mich war diese Probe die beste Lösung.

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

Also .. nur beim zweiten (Beispiel) emittieren - Sie sehen den zuletzt emittierten Wert von first (Quelle).

In meiner Aufgabe warte ich auf die Formularüberprüfung und andere DOM-Ereignisse.

Олег Беликов
quelle
1

Schauen Sie sich die Methode 'combinLatest' an, die hier möglicherweise angebracht ist. 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()
    })
Tal
quelle
0

Sie können 'zip' oder 'buffer' wie folgt verwenden.

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}`));
das seeyou
quelle