23

I am trying to return an observable after a successful completion of my Promise, but that function is not returning Observable. To be specific to code, i want to fetch auth token from storage (returns promise) and after that data got fetched then generate a Post request to Api (returns Observable). By doing so, sublime text gives an error on function that "a function whose declared type is neither 'void' nor 'any' must return a value" below is my code,

logout() : Observable<any>{
  this.userData.getAuthToken().then((token)=>{
    this.token = token;
    this.headers = new Headers ({
      "X-USER-TOKEN": token
    });
    this.options = new RequestOptions ({
      headers: this.headers
    });
    var logout_url = "Api logout method";
    return this.http.post(logout_url,{},this.options)
      .map (res => res.json())
  });
}

if i simply do a post request then it returns fine like this

return this.http.post(logout_url,{},this.options)
  .map (res => res.json())

but when i try to fetch data it do not return value from this post request. Any help will be much Appreciated! Thanks in advance

M. Habib
  • 623
  • 1
  • 9
  • 15

4 Answers4

51

Use fromPromise to convert the promise into an observable and use mergeMap to emit the HTTP response into the composed observable:

import { Observable } from 'rxjs/Observable/';
import 'rxjs/add/observable/fromPromise';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/mergeMap';

logout(): Observable<any>{
  return Observable.fromPromise(this.userData.getAuthToken()).mergeMap(token => {
    this.token = token;
    this.headers = new Headers({
      "X-USER-TOKEN": token
    });
    this.options = new RequestOptions({
      headers: this.headers
    });
    var logout_url = "Api logout method";
    return this.http.post(logout_url, {}, this.options).map(res => res.json());
  });
}
cartant
  • 57,105
  • 17
  • 163
  • 197
  • this solution looks good but it gives an error saying _TypeError: Observable_2.fromPromise is not a function._ Although i posted short hand version of my function in question. Function after updating with fromPromise and mergeMap is posted above. Plz take a look does it look fine ? – M. Habib Jan 11 '17 at 10:03
  • Added the necessary imports to the answer. – cartant Jan 11 '17 at 10:05
25

With RXJS>6, fromPromise is merged into from function and to use mergeMap, you need to use pipe first. So this code will work

    import { Observable, from } from "rxjs";
    import { map, mergeMap } from "rxjs/operators";
    
    logout(): Observable<any>{
      return from(this.userData.getAuthToken()).pipe(mergeMap(token => {
        this.token = token;
        this.headers = new Headers({
          "X-USER-TOKEN": token
        });
        this.options = new RequestOptions({
          headers: this.headers
        });
        var logout_url = "Api logout method";
        return this.http.post(logout_url, {}, this.options).map(res => res.json());
      }));
    }
lloiacono
  • 4,714
  • 2
  • 30
  • 46
Sunil Garg
  • 14,608
  • 25
  • 132
  • 189
2

You would be returning a Promise (if you hadn't missed the return part in front of it) which would return an Observable on success. Consider using Observables only, rather than mixing them both.

You can also encapsulate it into a new Observable: new Observable(observer =>
https://angular-2-training-book.rangle.io/handout/observables/using_observables.html

zurfyx
  • 31,043
  • 20
  • 111
  • 145
  • 1
    The promise is returning an observable,true.. But `logout` doesnt return anything.. – Suraj Rao Jan 11 '17 at 08:05
  • yes, ionic 2 storage returns promise of successful retrieval of data from storage and then angular 2 http post method returns Observable. the main thing is that how can i directly return something from inside promise success ? Although can you plz share code snippet of converting to Observable only ? it might help to solve the issue. – M. Habib Jan 11 '17 at 08:09
  • @M.Habib promises are async, you are already returning an `Observable` but you still have to wait for the `Promise` to finish. You can either do something like it shows on the example I linked or look at @cartant answer. – zurfyx Jan 11 '17 at 08:13
  • @zurfyx Can you please explain why promise returning observable. I expected return new Promise((resolve, reject) => {}); to return Promise but its returning Observable. – ani Aug 29 '21 at 12:19
1

Answer from Sunil worked for my use case, this is my working example. I am using this in my http interceptor to append acces token to the HTTP request

import {from, Observable} from "rxjs";
import {mergeMap} from "rxjs/operators";

 handleTokenExpiration(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    if (this.isAccessTokenPresent()) {
      request = request.clone({
        setHeaders: {
          Authorization: `Bearer ${this.getAccessToken()}`
        }
      });
      return next.handle(request);
    } else {
      if (this.isRefreshTokenPresent()) {
        return from(this.refreshToken(this.getRefreshToken())).pipe(mergeMap(res => {
          request = request.clone({
            setHeaders: {
              Authorization: `Bearer ${this.getAccessToken()}`
            }
          });
          return next.handle(request);
        }));
      } else {
        this.logout();
      }
    }
  }
Tomas Lukac
  • 1,923
  • 2
  • 19
  • 37