jhamm
jhamm

Reputation: 25032

How do I resolve an Observable inside of another Observable? - rxjs

I have an Observable in which I consume another observable, but the 2nd Observable I can't get to resolve. Here is the code:

return Observable.fromPromise(axios(config))
        .map(res => {
            return {
                accessToken: res.data.access_token,
                refreshToken: res.data.refresh_token                
            }
        })
        .map(res => {
            return {
                me: getMe(res.accessToken),
                accessToken: res.accessToken,
                refreshToken: res.refreshToken                
            }
        })

function getMe(accessToken) {
    return Observable.fromPromise(axios.get({
        url: 'https://api.spotify.com/v1/me',
    }));
}

The getMe function returns an Observable, but it is never resolved. I have tried to add a flatMap and a concat, but it still isn't resolved. How do I get the getMe to resolve?

Upvotes: 14

Views: 24243

Answers (3)

Jeremy
Jeremy

Reputation: 194

Did you try the following (Also untested):

function getMe(accessToken) {
  return Rx.Observable.fromPromise(axios.get({
    url: 'https://api.spotify.com/v1/me',
  }));
}    

Rx.Observable.fromPromise(axios(config))
    .map((res) => {
        return {
            accessToken: res.data.access_token,
            refreshToken: res.data.refresh_token                
        }
    })
    .flatMap((res) => {
        return getMe(res.accessToken).map((res2) => {
          res.me = res2;
          return res;
        }
    })
    .subscribe((data) => console.log(data));

As mentioned in the above post, flatMap returns an observable. map is subsequently used to merge res with the result res2 returned from the second promise.

Also note that fromPromise is a cold observable. This means that you must have a subscription to initiate things. In your case, I presume you already have something like this:

someFunction = () => {
  return Rx.Observable.fromPromise(axios(config))
     ...
     ...
}

someFunction.subscribe((data) => console.log(data));

Upvotes: 12

paulpdaniels
paulpdaniels

Reputation: 18663

As @user3743222 pointed out, an Observable does not resolve in the sense that a Promise does. If you want the value you of the getMe method you will need to subscribe to the Observable returned by it.

return Observable.fromPromise(axios(config))
        .map(res => {
            return {
                accessToken: res.data.access_token,
                refreshToken: res.data.refresh_token                
            }
        })
        .flatMap(function(tokens) {

          //FlatMap can implicitly accept a Promise return, so I showed that here
          //for brevity
          return axios.get({url : 'https://api.spotify.com/v1/me'});
        },

        //The second method gives you both the item passed into the first function
        //paired with every item emitted from the returned Observable`
        //i.e. axios.get(...)
        function(tokens, response) {
          return {
            accessToken: tokens.accessToken,
            refreshToken: tokens.accessToken,
            //Here response is a value not an Observable
            me: response
          };
        });

Upvotes: 2

user3743222
user3743222

Reputation: 18665

Sample code to find below (UNTESTED!!). Some explanations :

  • the observable returned by getMe is not flattened ('resolve' belong to the world of promises) because the map operator does not flatten observables. flatMap do, but you need to use it in the form source.flatMap(function(x){return observable}) and here what you return is a POJO not a Rx.Observable.
  • So, to flatten the getMe we use a flatMap.
  • To add back the missing fields (accessToken and refreshToken) we use withLatestFrom on the observable who emitted the res object (res$).
  • We use share as we subscribe twice to res$, and we want all subscribers to see the same values.

    var res$ = Observable
      .fromPromise(axios(config))
      .map(function ( res ) {
           return {
             accessToken  : res.data.access_token,
             refreshToken : res.data.refresh_token
           }
         })
      .share();
    var getMe$ = res$.flatMap(function ( res ) {return getMe(res.accessToken)});
    var finalRes$ = getMe$.withLatestFrom(res$, function ( getMe, res ) {
    return {
       me           : getMe,
       accessToken  : res.accessToken,
       refreshToken : res.refreshToken
     }
    });
    
    function getMe ( accessToken ) {
      return Observable.fromPromise(axios.get({url : 'https://api.spotify.com/v1/me'}));
    }
    

Upvotes: 0

Related Questions