Konrad Viltersten
Konrad Viltersten

Reputation: 39058

Combining multiple observables and acting when at least one value is returned in Angular

I have a set of observables and I'll need to react when the last one has provided a value (whichever that one will be).

My first approach was forkJoin like this.

const observables = [
  this.backend.config(),
  this.navigation.location()
];

forkJoin(observables)
  .subscribe(([backend, selection]) => {
    console.log(backend);
    console.log(selection);
  });

This worked partly as long as the observables emit complete at some point (upon which, the finalization is invoked. Then I tried combineLatest. That resolved the original issue but created a new one. Now, I get the final method invoked as soon as any of the observables provides.

I'd like to get it invoked when each of the observables has provided at least once and might or might not have completed. It doesn't mater if it's the latest value from each or if it's the first one. Although, it might be a plus to learn how to control that as well for +1.

The best, yet hacky, solution I came up with is to check !!backend and !!navigation and only act if all of them satisfy. I'm looking for a smoother solution or confirmation that the one I'm using is as good as it gets.

combineLatest(observables)
  .subscribe(([a, b, c]) => {
    if (!a || !b || !c)
      console.log("Oh, I'm coming...");
    else
      console.log("Wham, bam - I'm done!");
  });

All that from blogs like this or that, posts on SO and Observables' docs.

Upvotes: 6

Views: 4238

Answers (2)

Wilhelm Olejnik
Wilhelm Olejnik

Reputation: 2507

Use zip instead of forkJoin. It works similar but doesn't require observables to complete.

const a$ = of('a');                       // will complete instantly
const b$ = timer(2000).pipe(mapTo('b'));  // will complete later 
const c$ = new BehaviorSubject('c');      // will never complete

zip(a$, b$, c$).subscribe(console.log)    // (after 2s) ['a', 'b', 'c']

demo: https://stackblitz.com/edit/rxjs-ycqqhn

Upvotes: 3

Massimiliano Sartoretto
Massimiliano Sartoretto

Reputation: 1351

Well you describe the right operator:

combineLatest will not emit an initial value until each observable emits at least one value

So I believe your issue is different, maybe some of your sources emit a null/undefined value?

ref https://www.learnrxjs.io/operators/combination/combinelatest.html

Upvotes: 6

Related Questions