Dzhavat Ushev
Dzhavat Ushev

Reputation: 735

Using concat inside mergeMap

I have a hard time understanding something related to using a concat function inside a mergeMap operator.

The documentation for concat says that

You can pass either an array of Observables, or put them directly as arguments.

When I put the Observables directly as arguments, like in the following example, I correctly get 20 and 24 in the console.

of(4)
  .pipe(
    mergeMap(number => concat(of(5 * number), of(6 * number)))
  )
  .subscribe(value => console.log(value));

But when I put them as an array, then in the console I get the Observables and not their values:

of(4)
  .pipe(
    mergeMap(number => concat([of(5 * number), of(6 * number)]))
  )
  .subscribe(value => console.log(value));

Here's a live version in Stackblitz.

Any idea why is that? Shouldn't both examples work identically?

Upvotes: 1

Views: 1377

Answers (1)

LordTribual
LordTribual

Reputation: 4249

Those two scenarios are different and they should not work identically. concat takes Observables as arguments and it will sequentially subscribe to those streams and only subscribe to the next Observable when the previous one completed. Every operator or creation method returns an Observable. This means that in the first example, when you are using concat, it will return an Observable that emits 20 and then 24. Because you are dealing with a nested Observable you have to use mergeMap which will subscribe to the resulting Observable returned by concat.

Now in the second example, if you pass in an array, concat will convert this (using from() internally) to an Observable that emits 2 values, and those values are Observables again. So you have 3 levels of nesting here. The first is the most outer Observable, the source, which of(4), the second level is the one you map to inside your mergeMap and the third in the second example are the Observables inside your array. The thing is you only flatten the levels up to level 2 but not the 3rd level. Again, in your second example the Observable returned by mergeMap emits two Observables but those are just the proxies and not the values emitted by these Observables. If you want to subscribe to those as well, you could chain on another mergeMap like so

concatArray() {
  of(4)
    .pipe(
      mergeMap(number => concat([of(5 * number), of(6 * number)])),
      mergeMap(x => x)
    )
    .subscribe(value => console.log(value));
}

Another way is to spread the array so that concat does not receive an object that is ArrayLike but rather Observables directly:

concatArray() {
  of(4)
    .pipe(
      mergeMap(number => concat(...[of(5 * number), of(6 * number)]))
    )
    .subscribe(value => console.log(value));
}

Both will print out:

20
24

I hope this makes this a little bit more clear and describes the differences between the first and the second example.

Upvotes: 2

Related Questions