4

I have an array of observables that must be executed in order, but I need to return the set of observables cold (without subscribing) so that additional chaining can be performed.

const work = [observable1, observable2, observable3];

if I return concat(...work) and something attempts to chain from it work$.pipe(switchMap(() => ...)), the switchMap will be executed for each observable inside of concat - 3 times.

I'm looking for something similar to forkJoin where forkJoin(...work).pipe(switchMap(() => ...)) where the switchMap will only be executed once. But I can't just use forkJoin since the order must also be preserved.

The options I've found so far are:

  1. Return forkJoin(concat(...work)) technically works since the forkJoin will wait for the single concat to complete, but it also feels wrong since I'm only ever feeding forkJoin a single observable just for the side effect of it waiting for it to complete when it was made specifically to handle multiple observables.
  2. Return concat(...work).pipe(toArray()) maybe better than forkJoin but still feels like I'm missing a better operator. There is also takeLast() which functions roughly the same but still feels wrong since it seems to imply I care about the last value when I just want the completion. Almost something like finalize() but one that emits so it can be used to block the stream.

(Stackblitz examples here)

Is there a better operator for waiting for concat to complete, or just a better alternative to concat altogether that will execute the observables in order and let me chain the result like forkJoin does? Just feels like with the existence of forkJoin they have operators for waiting until multiple observables finish, and they have operators for maintaining order, there must be a basic one I'm missing that just waits for a source observable to complete and then emits.

JWrightII
  • 942
  • 11
  • 26
  • Idiomatic rxjs would say not to emit a value at all if youre not using them. The consumer can concat onto that if they want. return concat(...work).pipe(ignoreElements()) – Mrk Sef Apr 01 '23 at 00:44
  • Shouldn't you be able to use [`concatMap`](https://stackoverflow.com/a/72789311/8941307)? – Pieterjan Apr 01 '23 at 07:11
  • @MrkSef It's true, and honestly I might want the elements. I was mostly dismayed that there was no static operator like forkJoin which does emit all the results when complete. – JWrightII Apr 02 '23 at 03:45
  • @Pieterjan contactMap seems useful for chaining additional observables to each item in my source array, which doesn't really fit me need. – JWrightII Apr 02 '23 at 03:47
  • @Wrightboy An operator that emits all the results when complete: 'toArray()', but you have that already in your question, so I'm a bit unsure what you mean. – Mrk Sef Apr 02 '23 at 18:27

1 Answers1

0

I don't think there's anything wrong with the solutions you've come up with thus far, but perhaps you would find the following solution using concat and reduce better conveys your intent:

concat(...work).pipe(
    reduce(() => undefined)
);
  • concat says observables will be executed in order.
  • reduce emits once when its source emits
    • the () => undefined shows that you don't really care about the value

StackBlitz demo.

BizzyBob
  • 12,309
  • 4
  • 27
  • 51
  • Awesome! While I'd love if there was a static operator that performed the same thing, seeing "Reduces the values from source observable to a single value that's emitted when the source completes." is definitely the closest to actually seeming like I'm using an operator for it's intended purpose. Unless someone comes along with anything better this will be it. – JWrightII Mar 31 '23 at 22:50