8

I have two observables which I want to combine with combineLatest:

const o1 = from(['a', 'b', 'c']);
const o2 = of('content from o2');
combineLatest(o1, o2)
  .subscribe(result => console.log(result));

As I understood combineLatest, when any observable emits, it will combine the latest values of all observables and emit them:

When any observable emits a value, emit the latest value from each. (https://www.learnrxjs.io/operators/combination/combinelatest.html)

However, with the code above, the output is:

["c", "content from o2"]

So only when o2 emits, combineLatest emits an value.

If I switch the order I hand over o1 and o2, it works as expected:

const o1 = from(['a', 'b', 'c']);
const o2 = of('content from o2');
combineLatest(o2, o1)
  .subscribe(result => console.log(result));

Output:

["content from o2", "a"]
["content from o2", "b"]
["content from o2", "c"]

Is this expected behavior? If so, why? This seems to contradict the definition ob combineLatest.

fjc
  • 5,590
  • 17
  • 36

2 Answers2

7

Yes, in this case order matters, but it is a timing issue. combineLatest can not influence the order in which the values of the observables are emitted.

In your first case, o1 emits all values before o2 emits its value. In your second case o2 emits its value first and then o1 emits all its values. In real world examples you have a natural delay of observable values (service calls etc..) and so normally order does not matter.

René Winkler
  • 6,508
  • 7
  • 42
  • 69
-1

I cannot stress how useful I've found combineLatestObject from rxjs-etc by @cartant.

You provide an object with named observables and then instead of getting an array back you get an object with the named values.

combineLatestObject({

   isRaining: this.isRaining$,
   isSnowing: this.isSnowing$

}).pipe(map(({ isSnowing, isRaining }) => {

   return (isSnowing ? "It's snowing " : "It isn't snowing ") + "and " +
          (isRaining ? "it's raining " : "it isn't raining");

});

Inside map() or switchMap() you can deconstruct the object like I did here, or just provide a single parameter where you can access the values map(data => data.isSnowing).

It's so much safer and easier to read - and when I look back at old code I did I find it so hard to read. Note in the example above I've deliberately put the parameters in a different order and no issues doing so.

Why this isn't part of the main library I don't know!

Simon_Weaver
  • 140,023
  • 84
  • 646
  • 689
  • 2
    Nice, but you can actually deconstruct it this way too: ```combineLatest(this.isRaining$, this.isSnowing$).pipe(map([isRaining, isSnowing]) => {...});``` – pasevin Aug 31 '20 at 09:40