1
of([1,2,3]).subscribe(console.log)

prints:[1,2,3]

But:

of([1,2,3]).pipe(concatAll()).subscribe(console.log)

prints:

1
2
3

Why the above happens? Why adding concatAll() emits the elements of the array one by one? Isn't this somehow the opposite of what the word concat means?

I feel that concatAll() acts differently depending on the input.

Consider also this:

from([of(1),of(2),of(3)]).pipe(concatAll()).subscribe(console.log)

It will again print:

1
2
3

So of([1,2,3]).pipe(concatAll()) == from([of(1),of(2),of(3)]).pipe(concatAll())

But of([1,2,3]) != from([of(1),of(2),of(3)]) because subscribing to the latter will print:

Observable { _isScalar: false, _subscribe: [Function] }
Observable { _isScalar: false, _subscribe: [Function] }
Observable { _isScalar: false, _subscribe: [Function] }

The right side of the above equality is pretty clear to me, but where is documented that concatAll() should emit all the values of the array separately, acting like a pipeable from?

Marinos An
  • 9,481
  • 6
  • 63
  • 96

1 Answers1

2

All the operators that deal with higher-order observable operators, perform the same conversion that RxJS's from operator does.

that is:

mergeMap(_ => ([1,2,3]))

Is basically the same as

mergeMap(_ => from([1,2,3]))

That works the same when merging/switching/concatenating promises and other iterables.


The difference you're seeing in behaviour is also attributed to the fact that from creates a new observable, while the higher-order observable operators tend to create a new observables and define behaviour around how those observables are subscribed to.

so concatAll is adding extra behaviour.


Finally, just because two things have similar outputs for a given example, doesn't mean they're doing the same thing under the hood.

of([1,2,3]).pipe(concatAll()) and from([of(1),of(2),of(3)]).pipe(concatAll()) describe two very different set of behaviours that in (this case) give you the same output to the console.

The first emits an array and has that array turned into a stream of numbers by concatAll(). The second emits three observables, and has has concatAll() subscribe to each only when the previous one completes. They complete after emitting just one number.

You can see the distinction clearly with your second example:

of([1,2,3]) and from([of(1),of(2),of(3)])

The first emits and array, the second emits three observables. That hasn't changed.

concat([1,2,3]), &&
concat(of(1), of(2), of(3))

Emit the same output again as well.

Mrk Sef
  • 7,557
  • 1
  • 9
  • 21
  • Where does the documentation states that all higher-order observable operators do this `from` conversion? – Sebi2020 Jan 13 '22 at 17:02
  • @Sebi2020 Look here: https://rxjs.dev/api/operators/switchMap notice how project emits an `ObservableInput`? Then look https://rxjs.dev/api/index/type-alias/ObservableInput for what that means. Also look here: https://rxjs.dev/api/index/function/from to see that `from` takes the same thing. – Mrk Sef Jan 13 '22 at 17:20