Observable.forkJoin() doesn't execute
Asked Answered
W

5

35

I have the following code:

//Loop: For each user ID/Role ID, get the data
userMeta.forEach((businessRole) => {
  Observable.forkJoin(
    af.database.object('/roles/'+businessRole.$value),
    af.database.object('/users/'+businessRole.$key)
  ).subscribe(
    data => {
      console.log("Data received");
      data[1].role = data[0];
      this.users.push(data[1]);
    },
    err => console.error(err)
  );

I am trying to subscribe to a result of 2 observables using forkJoin.

For some reasons, the "Data received" message is not shown.

My userMeta variables looks fine at console.log:

enter image description here

What's wrong?

Update: the following code does not return anything either

let source = Observable.forkJoin(
        af.database.object('/roles/'+businessRole.$value),
        af.database.object('/users/'+businessRole.$key)
    );
    let subscription = source.subscribe(
      function (x) {
    console.log("GOT: " + x);
  },
  function (err) {
    console.log('Error: %s', err);
  },
  function () {
    console.log('Completed');
  });

What I actually trying to do is improve the performance of the following code:

//Subscription 3: role ID to role Name
        af.database.object('/roles/'+businessRole.$value)
        .subscribe((roleData) => {
        //Subscription 4: Get user info
        af.database.object('/users/'+businessRole.$key).subscribe(user => {
Wiser answered 28/10, 2016 at 10:19 Comment(3)
forkJoin() emits a value after both Observables complete, so are you sure they do? Maybe one of them end with an error...Krause
No errors too (see question update). They also must work because the former code was a subscribe inside a subscribe, and it workedWiser
forkJoin() doesn't pass errors from source Observables so this won't print anything even if it threw errors. If you want to make sure it doesn't emit errors you need to subscribe to each of the source Observables.Krause
K
34

forkJoin() requires all source Observables to emit at least once and to complete.

This following demo completes as expected:

const source = forkJoin(
  from([1,2,3]),
  from([9,8,7,6])
).subscribe(
  x => console.log('GOT:', x),
  err => console.log('Error:', err),
  () => console.log('Completed')
);

Live demo: https://stackblitz.com/edit/rxjs-urhkni

GOT: 3,6
Completed

Jan 2019: Updated for RxJS 6

Krause answered 28/10, 2016 at 19:5 Comment(6)
Did you checked my updated code in the end of question? because it works with the same observables, just not work forkJoin(). It's really strange...Wiser
@Wiser But that's not the same as what you want to do with forkJoin(). .subscribe((roleData) => {... emits a value on every next call. forkJoin(). forkJoin() requires both Observables to complete. otherwise it doesn't emit anything.Krause
Well it seem like I miss-used forkJoin(). Because your answer is corrcet, I'll approve it - and open a new question about my caseWiser
Observable.combineLatest() might be what you're looking for @TheUnreal.Mareld
@JakubBarczyk Thank you. If I could upvote you twice, I would.Barnaba
A clear example of forkJoin which details the complete method, great stuffGarett
W
29

Just add observer.complete();

Will not work:

observer.next(...)

Will work:

observer.next(...);
observer.complete();

Hope it helps.

Watershed answered 17/4, 2019 at 10:59 Comment(0)
C
21

I've faced a similar issue: I was creating a list of observables dynamically and I've noticed that forkjoin() never emits nor completes if the list of observables is empty whereas Promise.all() resolves with an empty list :

Observable.forkJoin([])
    .subscribe(() => console.log('do something here')); // This is never called

The workaround I've found is to check the length of the list and to not use this operator when it is empty.

return jobList.length ? Observable.forkJoin(jobList) : Observable.of([]);
Chamfron answered 6/3, 2017 at 10:18 Comment(1)
I have eventemitters for child components that fire dynamically. I am binding it to an output parameter. I have attached it to the parent and need to wait till all the observables complete.Enthusiasm
W
16

I had a similar issue using Angular 2 / Angularfire 2, specifically where I was looking up whether users existed by email. In one case, the user exists and I received an array of one object from the Observable. In the other case, the user did not exist, and I received an empty array.

When I used forkJoin with a resultSelector and a subscribe, neither the resultSelector nor the subscribe function ever ran. However, when I tried

Observable.zip(
  FirebaseListObservable,
  FirebaseListObservable,
  (...results) => {
    return results.map(some code here)
  }
).subscribe(res => console.log(res));

Both the selector and the subscribe worked. I assume this has to do with @martin's answer, where forkJoin requires the observables to complete, because by definition it returns the the last emissions. If an observable never completes, I suppose it can never have a last emission.

Perhaps angularfire list observables (or object observables in your case) never complete, making the use of forkJoin impossible. Fortunately zip has similar behavior and still works, the difference being that it can repeat several times if the data changes in Firebase, where as forkJoin only combines the last response.

In my case, I'm looking at either 1) using zip and accepting that my code might run multiple times if user data changes while the .zip is still running, 2) manually disable the zip after the first set of data returns, or 3) ditch Angularfire and try out the Firebase api directly, using something like .once to see if I can get an observable that completes and triggers forkJoin.

Waldenburg answered 15/2, 2017 at 8:11 Comment(3)
i had exactly the same issue, which could be fixed quite simple by using Observable.combineLatest instead of Observable.forkJoin. Credits go to github.com/angular/angularfire2/issues/617Songsongbird
Thanks for sharing this, it's actually very useful.Keitloa
Thank you, this was extremely helpful. I was doing a query on Firestore that may or may not return a value and when their was no value forkJoin would not return anything. I simply replaced forkJoin with combineLatest and it worked perfectly. Thank you for saving another hour of research.Offspring
P
11

I had the same issue and I could not really get the forkJoin operator work, so I just used the combineLatest, which has worked!

Phreno answered 29/9, 2020 at 18:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.