How to 'wait' for two observables in RxJS

gog picture gog · May 16, 2017 · Viewed 64.5k times · Source

In my app i have something like:

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

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

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

Then i get two separated results, first of the _personService and then the _documentService. How can I wait for both results before call this.showForm() to finish an then manipulate the results of each one.

Answer

Hamid Asghari picture Hamid Asghari · Jul 5, 2017

Last Update: May, 2020.

combineLatest(observables)

From reactiveX documentation:

Whenever any input Observable emits a value, it computes a formula using the latest values from all the inputs, then emits the output of that formula.

(Update: May, 2020) While the other example remains valid, here is a new 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();
       })

combineLatest(observables) (alternate 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 combineLatest

(Update: Oct, 2018) I previously suggested the use of zip method. However, for some use cases, combineLatest has a few advantages over zip. So it is important to understand the differences.

CombineLatest emits the latest emitted values from observables. While zip method emits the emitted items in sequence order.

For example if observable #1 emits its 3rd item and observable #2 has emitted its 5th item. The result using zip method will be the 3rd emitted values of both observables.

In this situation the result using combineLatest will be the 5th and 3rd. which feels more natural.


Observable.zip(observables)

(Original answer: Jul, 2017) Observable.zip method is explained in reactiveX documentation:

Combines multiple Observables to create an Observable whose values are calculated from the values, in order, of each of its input Observables.

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

a side note (applies for both methods)

The last parameter, where we have provided a function, (name: string, document: string) => ({name, document}) is optional. You can skip it, or do more complex operations:

If the latest parameter is a function, this function is used to compute the created value from the input values. Otherwise, an array of the input values is returned.

So if you skip the last part, you get an 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();
       })