Search code examples
javascriptangulartypescriptrxjs6feathersjs

Return Observable From Promise That Gets Rejected


I'm trying to determine how/if I can convert a Promise rejection into a typed Observable.

I have a login component with a function like this...

login() {
  const login$ = this.authenticationService.login(this.loginForm.value)
  login$
  .pipe(
    finalize(() => {
      this.loginForm.markAsPristine();
      this.isLoading = false;
    }),
    untilDestroyed(this)
  )
  .subscribe(
    (credentials) => {
      log.debug(`${credentials.username} successfully logged in`);
      this.router.navigate([this.route.snapshot.queryParams.redirect || '/'], { replaceUrl: true });
    },
    (error) => {
      log.debug(`Login error: ${error}`);
      this.error = error;
    }
  )
}

and an authentication service with a method like this...

login(context: LoginContext): Observable<Credentials> {

  var data = {} as Credentials
  
  // Call a method that returns a Promise
  this.server.authenticate(LoginContext)
  .then((result) -> {
      data.username = result.user.username
      data.token = result.accessToken
      this.credentialsService.setCredentials(data, context.remember)
      // Not sure what to do here...need to return data somehow as an Observable
      // return data
    })
  .catch((err) => {
      // Not sure what to do here either...need to bubble the err up so that it's caught in subscribe
      // return throwError(err)
    })
}

How can I return an Observable<Credentials> if the Promise resolves, and bubble up the error if it rejects?

I know I can make everything work if I change the authentication service function to return Promise<Observable<Credentials>>, which is fine, but for the sake of learning new things I'm trying to figure out if this is possible.


Solution

  • You can use the from function to convert any Promise to an Observable:

    import { from } from 'rxjs';
    
    // ...
    
    class YourClass {
      login(context: LoginContext): Observable<Credentials> {
        return from(this.server.authenticate(LoginContext).then(result => {
          const credentials: Credentials = {
            username: result.user.username,
            token: result.accessToken
          }; 
          this.credentialsService.setCredentials(credentials, context.remember);
          return credentials;
        }));
      }
    }