简体   繁体   中英

Observable subscription not getting called

I have an Observable that I'm using to convert a promise into a subscription. This results in a collection that I need to iterate through to call an HTTP Service on each element. I'm using forkJoin to wait for all those calls to finish so that I can do something else, but unfortunately, my subscription is not being called. Do you see what I'm missing here?

Observable.fromPromise(this.users.getElements()).subscribe(results => {
  Observable.forkJoin(
    results.map(
      aUser => this.HttpService.submitUser(aUser).subscribe(
        results => {
          this.progress += 1;
        },
        err => {
          this.progress += 1;
          this.handleError(<any>err);
        })
    ).subscribe(
      //it never gets to either of these calls after all service calls complete
      data => {
        debugger;
        console.log(data);
        this.reset();
      },
      err => {
        debugger;
        console.log(err);
        this.reset();
      }
    ));
});

One thing is you don't subscribe to each Observable passed to forkJoin() . The operator has to do it itself.

If you want to be notified when each Observable completes you can use .do(undefined, undefined, () => {...}) .

let observables = [
  Observable.of(42).do(undefined, undefined, () => console.log('done')),
  Observable.of('a').delay(100).do(undefined, undefined, () => console.log('done')),
  Observable.of(true).do(undefined, undefined, () => console.log('done')),
];

Observable.forkJoin(observables)
  .subscribe(results => console.log(results));

This prints to console:

done
done
done
[ 42, 'a', true ]

Eventually there's also .finally() operator. However, it's not the same as using .do() .

EDIT:

When any of the source Observables fail the forkJoin() operator reemits the error (which means it also fails).
This means you need to catch errors in each source Observable separately (with catch() operator for example).

let observables = [
  Observable.throw(new Error())
    .catch(() => Observable.of('caught error 1'))
    .do(undefined, undefined, () => console.log('done 1')),

  Observable.of('a')
    .delay(100).catch(() => Observable.of('caught error 2'))
    .do(undefined, undefined, () => console.log('done 2')),

  Observable.of(true)
    .catch(() => Observable.of('caught error 3'))
    .do(undefined, undefined, () => console.log('done 3')),
];

Observable.forkJoin(observables)
  .subscribe(results => console.log(results));

Which prints:

done 1
done 3
done 2
[ 'caught error 1', 'a', true ]

I don't think you need to subscribe in the map.

Observable.fromPromise(this.users.getElements()).subscribe(results => {
  Observable.forkJoin(
    results.map(
      aUser => this.HttpService.submitUser(aUser))
    ).subscribe(
      //it never gets to either of these calls after all service calls complete
      data => {
        debugger;
        console.log(data);
        this.reset();
      },
      err => {
        debugger;
        console.log(err);
        this.reset();
      }
    ));
});

Note that in the rxjs example here:

https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/forkjoin.md

they don't subscribe to the individual observables -- ForkJoin gets them all going, then waits for all of them to return (in your subscribe.)

EDIT:

The forkjoin source is here:

https://github.com/Reactive-Extensions/RxJS/blob/master/src/core/linq/observable/forkjoin.js

and it doesn't look like it has hooks to find out when each finishes. I think the best way to approach a UI bar would be to have each of the mapped observables subscribed to individually that all call a function to increment the UI counting bar variable and have some test for "completeness" that allows you to use the data.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM