简体   繁体   中英

rxjs/angular4 observable optimization

I have an optimisation issue on observables:

I have a two controls that accept an username and password. When the value of the password field changes (with a debounce), I want to call a service method that returns an observable of the query results and I fill a model with those values.

The returned observable throws an exception in case of service error. But I want to just fill the model with an empty array in such a case.

My original implementation:

  this.passwordControl.valueChanges
    .debounceTime(500)
    .filter(_ => this.usernameControl.valid && !this.usernameControl.pristine)
    .map((password) => new RequestAuthentication(this.usernameControl.value, password))
    .mergeMap(auth => this.orgService.getOrganizations(auth))
    .catch(_ => {
      return Observable.of([]);
    })
    .subscribe(orgs => {
      this.organizations = orgs;
    });

The drawback is that when the catch function is executed, I return a finite observable that completes the stream and I can't listen anymore to the changes.

My solution is to nest observables as such:

this.passwordControl.valueChanges
  .debounceTime(500)
  .filter(_ => this.usernameControl.valid && !this.usernameControl.pristine)
  .map((password) => new RequestAuthentication(this.usernameControl.value, password))
  .subscribe(auth => {
    this.orgService.getOrganizations(auth)
      .catch(_ => Observable.of([]))
      .subscribe(orgs => {
        this.organizations = orgs;
      });
  });

But this doesn't seem very reactive... How should I do to avoid these nested observables and keep the code clean ?

In general calling subscribe() inside another subscribe() isn't recommended because you're just exchanging "callback hell" with "subscribe hell". Also you're loosing error and complete signals from the parent Observable chain. Nonetheless, you can always avoid it.

So if you don't want to end the chain when an error occurs there're a few operators that can help you. Specifically: retry() and retryWhen() (also onErrorResumeNext() ) and probably more.

If you don't want to do anything when an error signal occurs use retry() instead of catch() :

.mergeMap(...)
.retry()
.subscribe(orgs => {
  this.organizations = orgs;
});

If you still want to be able to perform some side-effect (notify user or whatever) use .do() before retry() :

.mergeMap(...)
.do(null, e => ...)
.retry()
.subscribe(orgs => {
  this.organizations = orgs;
});

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