angular2 - Navigate after observable is complete

2020-02-17 13:52发布

问题:

I have an http observable like so, in my UserService:

logout() {
    return this.http.delete(this.baseUrl + url, {
          headers: this.headers()
        }).map((res: IResponse) => {
          var json = res.json();
          json.headers = res.headers;
          return json;
        }).subscribe((response) => {
          //DO SOMETHING, THEN ----
          return res;
        });
}

I have created an observable, and created a subscription (response) which is the returned success value.

Now, in my component, I want to call UserService.logout() and THEN navigate to a new route:

logout() {
    this.userService.logout();
    this.router.navigate(['LandingPage']);
  }

Obviously, this could happen asynchronously and I may end up navigating before I logout.

Using promises, I could do something like this:

this.userService.logout().then(() => { this.router.navigate(['LandingPage']); });

How can I do the same thing with observables? In my UserService class I want to create an observable, subscribe to it, do some stuff on success or on error, THEN navigate from my view component.

回答1:

You can actually make logout method return a promise and use it as normal. It doesn't have to be Observable:

logout() {
    return new Promise((resolve, reject) => {
        this.http.delete(this.baseUrl + url, { headers: this.headers() })
            .map((res: IResponse) => {
              var json = res.json();
              json.headers = res.headers;
              return json;
            })
            .subscribe(data => {
              //DO SOMETHING, THEN ----
              resolve(data);
            }, error => reject(error));
    });
}

logout() {
  this.userService.logout().then(response => this.router.navigate(['LandingPage']))
}


回答2:

Observable object has method finally, try it. Don't forget to import first:

import 'rxjs/operators/finally';

Your logout() would be:

 logout() {
  return this.http.delete(this.baseUrl + url, { headers: this.headers() })
    .map(res => res.json())
    .finally(() => this.router.navigate(['LandingPage']))
    .subscribe((response) => {
      //DO SOMETHING, THEN ----

    });
  }

If you really wanted to use promise:

import 'rxjs/add/operator/toPromise';

logout() {
  return this.http.delete(this.baseUrl + url, { headers: this.headers() })
    .map(res => res.json());

in component.ts

var aPromise = this.userService.logout().toPromise();
aPromise.then(() => {
    this.router.navigate(['LandingPage']);
});


回答3:

You can't wait for an Observable to finish because its stream might continue indefinitely. But you could call router.navigate inside the subscribe method:

logout() {
  return this.http.delete(this.baseUrl + url, { headers: this.headers() })
    .map((res: IResponse) => res.json())
    .subscribe((response) => {
      //DO SOMETHING, THEN ----
      this.router.navigate(['LandingPage']);
    });
  }

If this isn't sufficient, subscribe returns a Subscription whose unsubscribe method stops waiting for the Observable's data. So if you have to, you can wait for a timeout period and call unsubscribe.



回答4:

Logout need to return an Observable not a Subscription. For example:

    logout(): Observable<T> {
        return this.http.delete(this.baseUrl + url, {
              headers: this.headers()
            }).map((res: IResponse) => {
              var json = res.json();
              json.headers = res.headers;
              return json;
            })
            .catch((err)=>Observable.empty()) // return empty if error
            .share(); // to ensure that no duplicate http calls can occur
 
    }

    //in component:

    this.service.logout()
      .do(()=>this.router.navigate(['LandingPage']);)//do stuff
      .subscribe((res:any)=>{});

    //or with template async pipe:

    public result$ = Observable<T>;

    result$ = this.service.logout()
      .do(()=>//do stuff);

    //in template:
 
    {{(result$ | async)?.id}}
    or simply
    {{result$ | async}}

    // P.s. do not forget about unsubscribe(), thats why I prefer to use async (since that's the Async pipe's job.)

For more information about operators/methods: https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/observable.md